How does the implementation of ScalaNumber work regarding underlying?
scala.math.ScalaNumber
is a Java file which looks like this:
public abstract class ScalaNumber extends java.lang.Number {
protected abstract boolean isWhole();
public abstract Object underlying();
}
And scala.math.BigDecimal
implements it with:
class BigDecimal(val bigDecimal: BigDec, val mc: MathContext)
extends ScalaNumber with ScalaNumericConversions with Serializable {
...
def underlying = bigDecimal
}
as well as scala.math.BigInt
:
class BigInt(val bigInteger: BigInteger) extends ScalaNumber with ScalaNumericConversions with Serializable {
...
def underlying = bigInteger
}
Confusingly, underlying
is of type java.math.BigDecimal
/java.math.BigInt
instead of Object
.
Do I miss something pretty obvious or is there something special-cased here?
EDIT: Of course I missed something obvious ... You're all right. Co-vari开发者_JAVA技巧ant return types. Thanks!
It is simply a covariant return type, which is allowed both in Scala and Java.
The rationale behind it is: if a class Base
promises to return A
form a certain method, then a subclass Derived <: Base
respects the Liskov substitution principle if it returns A
or any subclass B <: A
. Certainly, if BigInt#underlying
returns a BigInteger
, this is no problem for clients of ScalaNumber
, who may only hope for a plain Object
.
In both Java and Scala method return types can be covariant when overriding. That is to say if you override a method you can make its return type be a subtype of the overriden method's return type.
scala> class Foo { def method : Object = "foo" }
defined class Foo
scala> class Bar extends Foo {override def method : String = "bar" }
defined class Bar
scala> (new Foo).method
res0: java.lang.Object = foo
scala> (new Bar).method
res1: String = bar
scala> ((new Bar) : Foo).method
res2: java.lang.Object = bar
精彩评论