gwtproject / gwt

GWT Open Source Project
http://www.gwtproject.org
1.52k stars 376 forks source link

String.valueOf gives ClassCastException #9439

Closed anilnf closed 8 years ago

anilnf commented 8 years ago

Failure in 2.8RC2 jar: (Same code works in gwt2.7) CodeSnippet which failed (passes once we remove String.valueOf):

public interface Properties {
    public <X> X get(String property);
}
public void onModuleLoad() {
    Properties prop = new Properties() {
        @Override
        public <X> X get(String property) {
            return (X) "Sdasdf";
        }
    };
    TextBox textBox = new TextBox();
    textBox.setValue("adgasdg" + String.valueOf(prop.get("yo")));
    RootPanel.get().add(textBox);
}

ClassCastException:

Throwable.java:121Uncaught Error: java.lang.ClassCastExceptionjjc_g$ @ Throwable.java:121tjc_g$ @ Throwable.java:113ajc_g$ @ Throwable.java:61Gjc_g$ @ Exception.java:25Ojc_g$ @ RuntimeException.java:25xpi_g$ @ ClassCastException.java:23xVj_g$ @ InternalPreconditions.java:141HVj_g$ @ InternalPreconditions.java:129tne_g$ @ Cast.java:75

rluble commented 8 years ago

That is probably due to changes in type inference in Java 8 (which for GWT relies on the java compiler library in eclipse JDT).

String.valueOf has many overloads and the call is quite ambiguous because the type parameter is only used in the return type. So some overload is selected and a cast to that parameter type is inserted due to type erasure (standard java).

Inserting a cast and rewriting to

  String.valueOf((String) prop.get("yo");

or having a variable

  String propValue = String.valueOf(...)

or making the type argument explicit

  String.<String>valueOf(...)

should remove the ambiguity from the call.

anilnf commented 8 years ago

This happens in run after build (as war) as well. Building with Java 8 as well as Java 7. Yes. The solutions you mentioned actually works.

On Mon 3 Oct, 2016, 9:36 PM rluble, notifications@github.com wrote:

That is probably due to changes in type inference in Java 8 (which for GWT relies on the java compiler library in eclipse JDT).

String.valueOf has many overloads and the call is quite ambiguous because the type parameter is only used in the return type. So some overload is selected and a cast to that parameter type is inserted due to type erasure (standard java).

Inserting a cast and rewriting to

String.valueOf((String) prop.get("yo");

or having a variable

String propValue = String.valueOf(...)

or making the type argument explicit

String.valueOf(...)

should remove the ambiguity from the call.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/gwtproject/gwt/issues/9439#issuecomment-251148563, or mute the thread https://github.com/notifications/unsubscribe-auth/AMMxWGpt40yijaLN1W2MCGR96kEiv_48ks5qwSgDgaJpZM4KMWEH .

rluble commented 8 years ago

FYI, GWT 2.8 always uses Java 8 mode independent of your IDE settings.

vteferrer commented 8 years ago

I've experimented the same behavior. I don't know if it should be considered an error but, in fact, in GWT 2.7.0 the same code works well but just changing to GWT 2.8 causes the ClassCastException has been fired:

public class Jee7 implements EntryPoint {
    static class GenericElement {
        public <VALUE> VALUE getValue() {
            return (VALUE)"Hola";
        }
    }

    public void test() {
        String value = String.valueOf(new GenericElement().getValue());
        Window.alert("value: " + value);
    }

    public void onModuleLoad() {
        try {
            test();
        } catch (ClassCastException e) {
            Window.alert("ClassCastException!");
        }
    }
}

Compiled with GWT 2.7.0 gwt27_generics_and_string_valueof

Compiled with GWT 2.8 gwt28_generics_and_string_valueof

I know that we can avoid the ClassCastException just by declaring explicitly the generic type or guiding the compiler type inference engine to the correct String.valueOf overloaded method. With this little change in the code, the error won't occur:

public void test() {
    String value = String.valueOf(new GenericElement().<Object>getValue());
    Window.alert("value: " + value);
}

But anyway, some code already tested and working in production environment compiled with GWT 2.7.0 could fail just compiling with the 2.8 version and, what is worse, in a silent way.

Should it be considered, in some way, a bug?

jnehlmeier commented 8 years ago

It's not a bug because you also get a ClassCastException when doing the same code in pure Java8 without any GWT involved, e.g.

public class Main {

    static class GenericElement {
        public <VALUE> VALUE getValue() {
            return (VALUE)"Hola";
        }
    }

    public static void main(String[] args) {
        String value = String.valueOf(new GenericElement().getValue());
        System.out.println("value: " + value);
    }
}

The above code compiles in Java 8 but fails at runtime with:

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to [C
    at test.Main.main(Main.java:12)
tbroyer commented 8 years ago

Confirmed: https://ideone.com/1BoRrV (Java 8) vs. http://www.browxy.com/SubmittedCode/647675 (Java 7)

rluble commented 8 years ago

It is quite surprising that Java semantics don't flag this call as ambiguous or choose String.valueOf(Object) as the appropriate overload.

vteferrer commented 8 years ago

I totally agree @rluble

tbroyer commented 8 years ago

Java overload resolution chooses the most specific overload, and because you let it "choose" the generic type argument, it's "free" to choose the char[] overload over the Object one (actually no "free", but following the algorithms from the JLS, as both javac and jdt behave the same way)

rluble commented 8 years ago

On the bright side javac will warn you of the unsafe behavior of methods declared with a free type variable in the return type. To be able to construct a method with a free type variable in the return type you need to have an unsafe cast (unless you always return null).

I take this as an unwanted interaction between the "most specific overload rule" and "left-to-right" aspect of Java type inference. I wonder if the other sensible choice (applying the most specific overload rule based on the erasure of free type variables and then perform type inference) has also some unwanted interactions.

FWIW, Errorprone has a more descriptive warning for this scenario (http://errorprone.info/bugpattern/TypeParameterUnusedInFormals)

petekos commented 6 years ago

is there anyway to get warned about all the potential exceptions of this type in a gwt codebase ?

rluble commented 6 years ago

That is the reason it is not a good practice to declare a type variable that is only used on the return type. You can use errorprone to warn you about this one and other potential pitfalls.