The following snippet throws NullPointerException. Is it expected and normal behavior of Scala?
object ATest extends App {
def getX[T <: X](constr: ⇒ T = null.asInstanceOf[T]): Unit = {
constr
}
getX()
}
class X
Generated (decompied) Java code from snippet:
public final class ATest {
public static void main(String[] arrstring) {
ATest$.MODULE$.main(arrstring);
}
public static void delayedInit(Function0<BoxedUnit> function0) {
ATest$.MODULE$.delayedInit(function0);
}
public static String[] args() {
return ATest$.MODULE$.args();
}
public static void scala$App$_setter_$executionStart_$eq(long l) {
ATest$.MODULE$.scala$App$_setter_$executionStart_$eq(l);
}
public static long executionStart() {
return ATest$.MODULE$.executionStart();
}
public static void delayedEndpoint$test$ATest$1() {
ATest$.MODULE$.delayedEndpoint$test$ATest$1();
}
public static <T extends X> T getX$default$1() {
return ATest$.MODULE$.getX$default$1();
}
public static <T extends X> void getX(Function0<T> function0) {
ATest$.MODULE$.getX(function0);
}
}
public final class ATest$ implements App {
public static final ATest$ MODULE$;
private final long executionStart;
private String[] scala$App$$_args;
private final ListBuffer<Function0<BoxedUnit>> scala$App$$initCode;
public static {
new test.ATest$();
}
public long executionStart() {
return this.executionStart;
}
public String[] scala$App$$_args() {
return this.scala$App$$_args;
}
public void scala$App$$_args_$eq(String[] x$1) {
this.scala$App$$_args = x$1;
}
public ListBuffer<Function0<BoxedUnit>> scala$App$$initCode() {
return this.scala$App$$initCode;
}
public void scala$App$_setter_$executionStart_$eq(long x$1) {
this.executionStart = x$1;
}
public void scala$App$_setter_$scala$App$$initCode_$eq(ListBuffer x$1) {
this.scala$App$$initCode = x$1;
}
public String[] args() {
return App.class.args((App)this);
}
public void delayedInit(Function0<BoxedUnit> body) {
App.class.delayedInit((App)this, body);
}
public void main(String[] args) {
App.class.main((App)this, (String[])args);
}
public <T extends X> void getX(Function0<T> constr) {
constr.apply();
}
public <T extends X> T getX$default$1() {
return null;
}
public final void delayedEndpoint$test$ATest$1() {
this.getX((Function0<T>)new scala.Serializable(){
public static final long serialVersionUID = 0;
public final Nothing. apply() {
return (Nothing.)ATest$.MODULE$.getX$default$1();
}
});
}
private ATest$() {
MODULE$ = this;
App.class.$init$((App)this);
this.delayedInit((Function0<BoxedUnit>)new ATest.delayedInit$body(this));
}
}
public final class ATest$.anonfun extends AbstractFunction0<Nothing.>implements Serializable {
public final Nothing. apply() {
return (Nothing.)ATest$.MODULE$.getX$default$1();
}
}
And finally action part:
public <T extends X> void getX(Function0<T> constr) {
constr.apply();
}
public <T extends X> T getX$default$1() {
return null;
}
public final void delayedEndpoint$test$ATest$1() {
this.getX((Function0<T>)new scala.Serializable(){
public final Nothing. apply() {
return (Nothing.)ATest$.MODULE$.getX$default$1();
}
});
}
That is: call to getX passes new anon Function0 which apply() just calls getX$default$1() that is null. So i can not see any point where NPE can be thrown.
EDIT: The unresolved issue is found: https://issues.scala-lang.org/browse/SI-8097
EDIT: Expression null.asInstanceOf[T] generates default value for type T. In case when Scala infers resulting type T as Nothing we come to runtime expression
null.asInstanceOf[Nothing]
that obviously throws Exeption as a default for Nothing is Exception.
But than why this snippet throws NPE only at last line?
object ATest extends App {
def getX[T](x: T = null.asInstanceOf[T]): T = x
getX[Nothing]() // Ok
val x = getX() // Ok
val y = null
println("x= "+x) // prints 'x= null'
println(s"y= $y") // prints 'y= null'
println(s"x= $x") // throws NPE !?
println("x==null ? "+(x==null)) // prints 'x= null'
}
And why this snippet throws NPE (it is only different from previous in implicit param)?
object ATest extends App {
def getX[T](x: T = null.asInstanceOf[T])(implicit s: String = null): T = x
getX() // throws NPE !?
}
So the situation is still vague. And question is open.