I am reading "Scala for the Impatient" and in 8.8 they say:
[..] you can use the abstract keyword to denote a class that cannot be instantiated [..]
abstract class Person { val id: Int ; var name: String }
And several lines later:
You can always customize an abstract field by using an anonymous type:
val fred = new Person {
val id = 1729
var name = "Fred"
}
So, they artificially instantiated Person class with the anonymous type. In which situations in the real world one would want to do it?
After thinking about my own answer a little bit, I concluded that all it says is essentially just:
"Anonymous local class instances are poor man's function literals"
Offered a +150
bounty for an answer that helps expand this narrow vision.
TL;DR
Whenever you want to treat implementations of methods as objects, you can instantiate an anonymous local class that extends an abstract base class, implement the methods, and then pass the created instance around just like any other instance of the base class.
Overview
This posting discusses five situations in which you might want to instantiate anonymous local classes. The examples progress from very basic to fairly advanced.
Runnable
Function<X, Y>
Disclaimer: some of the code is non-idiomatic, because it "reinvents the wheel" and does not hide the instantiation of abstract local classes in lambdas or SingleAbstractMethod-syntax.
Simple introductory example: Runnable
Suppose that you want to write a method that takes some block of code, and executes it multiple times:
def repeat(numTimes: Int, whatToDo: <someCleverType>): Unit = ???
Assuming that you want to reinvent everything from scratch, and do not want to use any by-name parameters or interfaces from standard libraries, what do you put in place of <someCleverType>
? You would have to provide your base class that looks somewhat like this:
abstract class MyRunnable {
def run(): Unit // abstract method
}
Now you can implement your repeat
method as follows:
def repeat(numTimes: Int, r: MyRunnable): Unit = {
for (i <- 1 to numTimes) {
r.run()
}
}
Now suppose that you want to use this method to print "Hello, world!" ten times. How do you create the right MyRunnable
? You could define a class
HelloWorld
that extends MyRunnable
and implements the run
method, but it would only pollute the namespace, because you want to use it only once. Instead, you can instantiate an anonymous class directly:
val helloWorld = new MyRunnable {
def run(): Unit = println("Hello, world!")
}
and then pass it to repeat
:
repeat(10, helloWorld)
You could even omit the helloWorld
variable:
repeat(10, new MyRunnable {
def run(): Unit = println("Hello, world!")
})
This is a canonical example of why you would want to instantiate anonymous local classes.
Slightly more interesting example: RealFunction
In the previous example, the run
took no arguments, it executed the same code every time.
Now I want to modify the example slightly, so that the implemented method takes some parameters.
I will not provide full implementations now, but suppose that you have a function
plot(f: RealFunction): Unit = ???
that plots a graph of a real function R -> R
, where RealFunction
is an abstract class defined as
abstract class RealFunction {
def apply(x: Double): Double
}
To plot a parabola, you could now do the following:
val xSquare = new RealFunction {
def apply(x: Double): Double = x * x
}
plot(xSquare)
You could even test it separately without plot
: for example, p(42)
computes 1764.0
, which is the square of 42
.
General functions Function[X, Y]
The previous example generalizes to arbitrary functions, which can have types X
and Y
as domain and codomain. This is arguably the most important example from the historical point of view. Consider the following abstract class:
abstract class Function[X, Y] {
def apply(x: X): Y // abstract method
}
It is similar to the RealFunction
, but instead of fixed Double
, you now have X
and Y
.
Given this interface, you could re-create the xSquare
function as follows:
val xSquare = new Function[Double, Double] {
def apply(x: Double) = x * x
}
Indeed, this example is so important that Scala's standard library is filled with such interfaces FunctionN[X1,...,XN, Y]
for varying numbers of arguments N
.
These interfaces get their own concise syntax and are otherwise heavily privileged in the compiler. This creates a "problem" from the point of view of your question, because the instantiation of anonymous classes is usually hidden under special built-in syntactic sugar. In idiomatic Scala, you would usually simply write
val xSquare = (x: Double) => x * x
instead of
val xSquare = new Function[Double, Double] {
def apply(x: Double) = x * x
}
The situation is similar in other JVM languages. For example, even Java version 8 introduced bunch of very similar interfaces in java.util.function
.
Few years ago, you would have written something like
Function<Integer, Integer> f = new Function<Integer, Integer>() {
public Integer apply(Integer x) {
return x * x;
}
};
in Java, because there were no lambdas yet, and every time you wanted to pass some kind of callback or Runnable
or Function
, you had to implement an anonymous class that extends an abstract class. Nowadays, in newer Java versions it is hidden by the lambdas and the SingleAbstractMethod-syntax, but the principle is still the same: the construction of instances of anonymous classes implementing an interface or extending an abstract class.
An advanced "almost-real-world"-example
You will not encounter any of the previous examples in the code written today, because the instantiation of anonymous local classes is hidden by syntactic sugar for lambdas. I want to provide a realistic example where the instantiation of anonymous local classes is actually unavoidable.
The new AbstractClassName(){ }
-syntax still appears where no syntactic sugar is available. For example, because Scala has no syntax for polymorphic lambdas, to construct a natural transformation in a library like Scalaz or Cats, you would usually write something like:
val nat = new (Foo ~> Bar) {
def apply[X](x: Foo[X]): Bar[X] = ???
}
Here, Foo
and Bar
would be something like embedded domain specific languages that operate on different levels of abstraction, and Foo
is more high-level, whereas Bar
is more low-level. It's exactly the same principle again, and such examples are everywhere. Here is an almost "photo-realistic" example of real-world usage: defining an (KVStoreA ~> Id)
-interpreter. I hope that you can recognize the new (KVStoreA ~> Id) { def apply(...) ... }
part in there. Unfortunately, the example is fairly advanced, but as I mentioned in the comments, all the simple and frequently used examples have been mostly hidden by lambdas and Single-Abstract-Method syntax over the past decade.
Back to your example
The code that you quoted
abstract class Person(val name: String) {
def id: Int
}
val fred = new Person {
val id = 1729
var name = "Fred"
}
does not seem to compile, because the constructor argument is missing.
My guess is that the author wanted to demonstrate that you can override def
s by val
s:
trait P {
def name: String
}
val inst = new P {
val name = "Fred"
}
While it's good to know that this is possible, I don't consider this the most important use case for anonymous local class instantiation (because you could have used an ordinary member variable and pass the value in the constructor instead). Given the space constraints, the author of the book probably just wanted to quickly demonstrate the syntax, without going into extended discussions of the real-world usage of that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With