scalainheritanceerasure

abstract type T is unchecked since it is eliminated by erasure


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?


Solution

  • 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.