3
votes

I am writing a Scala class which extends a Java class. I must extend this abstract Java class because it contains some converters, and I need to write a converter of my own that I can plug into their framework. So the signature of the method below is forced upon me: it is generated automatically by Eclipse when I say that my class extends their abstract class. If I try to modify the method signature, the class fails to compile because "it does not implement the abstract method from the parent".

The implementation of the method is my choice, of course - but I find that I cannot satisfy the constraints of the type system.

I need to do something very simple: treat "None" as null and return anything else as is. But I find that I cannot do this in a type-safe way. If I write the code as shown below, I get a warning saying that "abstract type T is unchecked since it is eliminated by erasure". How can I eliminate that?

I also wonder what my code is going to look like once erasure if applied to it, i.e. what the second "if" condition is going to look like.

Intuitively, I understand that I cannot guarantee to return a T when the input is Any - which is why the compiler is unhappy. In practice, this will work, because we are just reading strings and returning strings - but the method signature is forced upon me by the inheritance declaration.

  def execute[T](value: Any): T = {
    if (value == null || value == "None") null.asInstanceOf[T]
    // Warning: abstract type T is unchecked since it is eliminated by erasure
    else if (value.isInstanceOf[T]) value.asInstanceOf[T]
    //            warning here: ^
    else null.asInstanceOf[T]
  }

How can I implement this method?

2
Since erasure turns the second "if" statement into "isInstanceOf[Object]", which is always true, this is actually guaranteed not to do what the programmer intended. I am shocked that this is a warning and not an error. I realize that this problem cannot be solved in a type-safe way, because the input is Any and the output is T. What I realize from Alexey's solution is that once you start doing things like this, you are basically telling the compiler "trust me, at run time this will be castable to a T", and the compiler's "type safety guarantee" becomes "I see that the programmer promised a T".radumanolescu

2 Answers

2
votes

Your Java solution doesn't contain a test value instanceof T, so you don't need isInstanceOf[T] in the Scala solution. And you should remove it because the check doesn't do anything: it erases to isInstanceOf[Object], which is always true, so the last else branch is never taken. The remaining difference is that in Java you are comparing String.valueOf(value) with "None", not value itself. So the equivalent of the Java solution is

def execute[T](value: Any): T = {
  (if (value == null || String.valueOf(value) == "None") null else value).asInstanceOf[T]
}

The condition can be simplified to value == null || value.toString == "None", by definition of String.valueOf.

IMO, the compiler should give a warning for asInstanceOf[T] and not just for isInstanceOf[T]; this is a case where Java is actually safer than Scala because 1) it does give the warning for the cast; 2) the instanceof check would be an error instead of a warning.

As a side note, while you can get rid of the warning, presence of a method with a signature like this strongly indicates that the author didn't understand Java generics well enough and I would be quite careful about relying on that library.

1
votes

In the end, I can do one of two things:

  1. implement in Java:

    @SuppressWarnings("unchecked") @Override public T execute(Object value) { if (value == null || "None".equals(String.valueOf(value))) return null; else return (T) value; }

  2. implement in Scala and add @unchecked

    def execute[T](value: Any): T = { if (value == null || value == "None") null.asInstanceOf[T] else if (value.isInstanceOf[T @unchecked]) value.asInstanceOf[T] else null.asInstanceOf[T] }

I still wonder what is left of the second if statement after erasure.

Thanks to Alec for the suggestion!