I am starting to learn Scala and I will do a simple cross compiler.
I will support a small set of instructions like print.
Note: the code snippets are not tested or compiled.
Here is what I would do in JAVA.
public interface Compiler{
String getPrintInstruction();
}
public class JavaCompiler implements Compiler{
public String getPrintInstruction(){
return "System.out.print(arg0);"
}
}
public class ScalaCompiler implements Compiler{
public String getPrintInstruction(){
return "print(arg0);"
}
}
Is the snippet below the correct "Scala way"?
trait Compiler {
var printInstruction: String
}
class JavaCompiler extends Compiler {
var printInstruction = "System.out.print(arg0);"
}
class ScalaCompiler extends Compiler {
var printInstruction = "print(arg0);"
}
EDIT:
I will move my second question to a new thread.
For a 1:1 mapping, those var
s should be changed to def
s.
trait Compiler {
def printInstruction: String
}
class JavaCompiler extends Compiler {
def printInstruction = "System.out.print(arg0);"
}
class ScalaCompiler extends Compiler {
def printInstruction = "print(arg0);"
}
def
declares a method. When you don't provide an implementation, it becomes an abstract method.
EDIT:
The technique used here is a valid and useful technique. Alternatively you could use one of the following two techniques to model your problem.
1) Discriminated unions. (aka sum types.)
Refer to this excellent article to learn about this concept. This is how your example would probably look like when modeled this way:
sealed trait Compiler {
def printInstruction: String = this match {
case JavaCompiler => "System.out.print(arg0);"
case ScalaCompiler => "print(arg0);"
}
}
case object JavaCompiler extends Compiler
case object ScalaCompiler extends Compiler
2) Type class pattern.
Here is a great post by Daniel Sobral on this topic. You can dig up a few more by googling the terms type-class, pattern, Scala, implicits etc. This is how your code might look like if the problem's modeled with type class pattern:
trait Compiler[C] {
def printInstruction(c: C): String
}
case object JavaCompiler
implicit object JavaCompilerIsCompiler extends Compiler[JavaCompiler.type] {
def printInstruction(c: JavaCompiler.type): String = "System.out.print(arg0);"
}
case object ScalaCompiler
implicit object ScalaCompilerIsCompiler extends Compiler[ScalaCompiler.type] {
def printInstruction(c: ScalaCompiler.type) = "print(arg0);"
}
For your problem, the original approach and the discriminated unions approach seem to be the best modeling solutions.
The most idiomatic way is to use a def
for abstract properties, and a val
for concrete read-only properties. Under the Uniform Access Principle, a val
can be used to implement a method:
trait Compiler {
def printInstruction: String
}
class JavaCompiler extends Compiler {
val printInstruction = "System.out.print(arg0);"
}
class ScalaCompiler extends Compiler {
val printInstruction = "print(arg0);"
}
Why do I have to re declare the variable one more time in the class when I have declared it in the trait?
Because you declared the signature of the method printInstruction
but you did not say what it did. In a class
, as it is not an abstract class
all functions should be defined.
By the way, you could have defined printInstruction
directly in the trait Compiler
if it is supposed to do the same in every implementation.
来源:https://stackoverflow.com/questions/9033253/is-this-the-correct-way-of-translating-java-interface-into-scala