Below code throws a java.lang.ClassCastException if I directly use returned object of t.getTheString(implClass.class) in System.out.println() . Although when I assign object to some reference , it works fine, Why ?
interface testInterface{
public String testMethod();
}
class testClass{
public <T extends testInterface> T getTheString(Class< ? extends testInterface> classType){
try {
testInterface obj = classType.newInstance();
return (T)obj;
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
class implClass implements testInterface{
#Override
public String testMethod() {
return "Sample String";
}
#Override
public String toString(){
return super.toString() + " Overridden toString()";
}
}
public class genericTest {
public static void main(String args[]){
testClass t = new testClass();
testInterface ti = t.getTheString(implClass.class);
implClass i = t.getTheString(implClass.class);
//System.out.println(i); //<= Works fine
//System.out.println(ti); //<= Works fine
System.out.println(t.getTheString(implClass.class)); // Exception in thread "main" java.lang.ClassCastException: implClass cannot be cast to java.lang.String
}
}
Edit: Many people are getting confused with the question. The use of upper bound is just to create this example . I know this is incorrect .
The doubt is , if you decompile above code , you will see the compiler typecast the t.getTheString(implClass.class) result to String because may be it is unsure of the type probably . My question is wont it be better if it is typecast to Object . In that case it will call toString() method and code will work fine .
Is this an issue with Java Compiler or am i missing any thing here ?
The causes of the problem is already mentioned in other answers - your classType parameter can be any type that implements testInterface, not necessarily T.
Here's a solution.
The problem currently is that classType is not constrained, and without enough context, T cannot be inferred correctly, causing obj to be casted to a wrong type. So if we change the method to this:
public <T extends testInterface> T getTheString(Class<T> classType){
T can be inferred correctly every time from the parameter!
Another solution would be to spoonfeed the compiler with the type you want:
System.out.println((testInterface)t.getTheString(implClass.class));
From your comment to another answer:
If compiler is uncertain about the type why not type cast it to Object
instead of String
There are only two overloads of println that the compiler can choose to call because the others are primitive types. It's either println(Object) or println(String). According to the JLS (section 15.12.2):
This step uses the name of the method and the types of the argument expressions to locate methods that are both accessible and applicable There may be more than one such method, in which case the most specific one is chosen.
See this post for more info.
Since String is more specific than Object, the latter is chosen.
Generics are erased at runtime, so what happens is that when JVM needs to choose which variant of System.out.println() to call it has no clue what impl it is and assumes incoming class is String and takes System.out.println(String string) and obviously your implClass instance is not String.
This all comes from you using generics, but not declaring them at class level. Change testClass declaration to declare your generic T
class testClass<T> {
once you do, compiler should start complaining about missing cast when trying to convert testInterface to implClass at
implClass i = t.getTheString(implClass.class);
once you correct it like:
implClass i = (implClass) t.getTheString(implClass.class);
the System.out.println() will start working as expected.
If you are wondering why compiler makes wrong choice of method, think about this:
- Type T is not declared at class level, so when it is erased during compilation, it becomes "anything",
- when compiler is later on trying to find out matching variant of println() method to call, it takes first that matches, since type has become "anything", the first method variant will match
- and in Oracle JDK impl of PrintStream, first impl happens to be the one with String as parameter (check the source code ;)
Works fine for me; renamed and ran as Main.java:
> javac Main.java
Note: Main.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
> java Main
implClass#15dc8287 Overridden toString()
Then:
> javac -Xlint:unchecked Main.java
Main.java:10: warning: [unchecked] unchecked cast
return (T)obj;
^
required: T
found: testInterface
where T is a type-variable:
T extends testInterface declared in method <T>getTheString(Class<? extends testInterface>)
The problem here: in your method getTheString() you cast to T; but there is no guarantee that the incoming class has anything to do with that T! A class that implements that interface isn't necessarily also a T.
And for the exception that you see; as said - not reproducible (some setup problem on your side)?
If you un-comment the two "it works" lines and run genericTest.class through a decompiler (I used http://www.javadecompilers.com/), you'll see:
public static void main(String[] arrstring) {
testClass testClass2 = new testClass();
Object t = testClass2.getTheString(implClass.class);
implClass implClass2 = (implClass)testClass2.getTheString(implClass.class);
System.out.println((Object)implClass2);
System.out.println(t);
System.out.println((String)testClass2.getTheString(implClass.class));
}
The first two calls to println call the method that accepts an Object, which in turn calls toString() and prints the result you expect. In that third println, the compiler expects the result to really be a String, and attempts the cast, which is where you're getting the exception from.
Related
I'm trying to run a code. i and i get two compilation errors:
1.Reference to System.out.println is ambiguous (conflict between method that gets char[] and a method that gets a String)
2.Cap#1 can't converted to T return st.pop()
import java.util.*;
public class Test
{
public static void main(String[] args)
{
Stack <Number> stackNumber = new Stack<Number>();
Test t = new Test();
t.setMethod(stackNumber,new Integer(3));
System.out.println(t.getMethod(stackNumber));
}
public <T extends Number> void setMethod (Stack<? super Number>st,T t)
{
st.add(t);
}
public <T>T getMethod (Stack<? extends Number >st)
{
return st.pop();
}
}
I know that i can change getMethod signature to return Number and program will be compiled successfully but i want to understand why with current signature i'm getting compilation errors?
AFAIK, T without bounds considered as Object and a function that declares to return Object can return any Object since Object is the "Father" of all classes (including Number). Can someone me what i'm dismissing here?
Neither of your methods should be using wildcard captures, you have two methods that are generic against some T. Like,
public <T> void setMethod(Stack<T> st, T t) {
st.add(t);
}
public <T> T getMethod(Stack<T> st) {
return st.pop();
}
If you want to ensure that T must be a Number for some reason (I would just use Number then), you define it at T. Like,
public <T extends Number> void setMethod(Stack<T> st, T t) {
st.add(t);
}
public <T extends Number> T getMethod(Stack<T> st) {
return st.pop();
}
but i want to understand why with current signature i'm getting compilation errors?
The errors are both because <T> is determined at the call site.
Looking at compilation error 1:
Java selects the most specifically-applicable method. Any of the PrintStream.println methods that take a reference-typed parameter could be selected.
From JLS 15.12.2.5:
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time error.
Anything that you can pass to println(char[]) or println(String) can also be passed to println(Object), therefore the former methods are more specific than the latter. As such, these will be selected in preference to println(Object).
However, some things that can be passed to println(char[]) cannot be passed to println(String), therefore neither of those is more specific than the other, hence the ambiguous method call.
Now looking at compilation error 2:
public <T>T getMethod (Stack<? extends Number >st)
{
return st.pop();
}
This method must be safe to invoke in all situations. You invoke it like this:
System.out.println(t.getMethod(stackNumber));
i.e. you treat the result simply like an object. But you could, legally, write this at the call site:
String s = t.getMethod(stackNumber);
It's hopefully clear that this would fail, because something popped out of a stack containing numbers can't be cast to a String.
Because the compiler can't guarantee that it will be called with a "safe" T, it's an error.
I'm getting this strange exception in code run using jre1.8.0_66:
Exception in thread "main" java.lang.BootstrapMethodError: call site initialization exception
at java.lang.invoke.CallSite.makeSite(CallSite.java:341)
at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)
at main
Caused by: java.lang.invoke.LambdaConversionException: Invalid receiver type class java.lang.Object; not a subtype of implementation type interface Fruit
at java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:233)
at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:303)
at java.lang.invoke.CallSite.makeSite(CallSite.java:302)
... 3 more
What's it mean? The code is as follows:
public static interface Fruit {
int getPickingMonth();
}
public static class Apple implements Fruit, Serializable {
#Override
public int getPickingMonth() {
return 11;
}
}
public static class Orange implements Fruit, Serializable {
#Override
public int getPickingMonth() {
return 2;
}
}
public static void main(String[] args) {
List<Apple> apples = Arrays.asList(new Apple());
List<Orange> oranges = Arrays.asList(new Orange());
Stream.of(apples.stream(), oranges.stream())
.flatMap(Function.identity())
.map(Fruit::getPickingMonth) // exception occurs on this line
.forEachOrdered(System.out::println);
}
The exception goes away if I change Fruit::getPickingMonth to x -> x.getPickingMonth().
For what it's worth: The exception also goes away if I remove Serializable from either class. But returns if I add another, equivalent interface to both classes, e.g. Cloneable or some custom interface.
You ran into the same compiler bug that has been discussed in this question and that question.
The problem occurs whenever an intersection type is involved and you are using a method reference using a receiver type other than the first one (the first type is the one that will remain after type erasure).
So when you replace the method reference with a lambda expression, you are not affected by the bug anymore. If you remove the Serializable from the types instead, the inferred element type of the Stream will be Fruit, i.e. not an intersection type, and again the problem does not occur. But with the two element types implementing Fruit and Serializable, the compiler will infer the element type Object&Fruit&Serializable and the raw type will be Object which provokes the error when using a method reference with the receiver type Fruit. You can easily work around this:
Stream.of(apples.stream(), oranges.stream())
.<Fruit>flatMap(Function.identity())
.map(Fruit::getPickingMonth) // no more exception on this line
.forEachOrdered(System.out::println);
The compiled code will be identical to your original, but the formal result type of the flatMap operation will be Stream<Fruit>, ignoring all other artifacts of the inferred intersection type. As a consequence the method reference Fruit::getPickingMonth will implement the type Function<Fruit,Integer> instead of Function<Object&Fruit&Serializable,Integer> and the compiler bug does not materialize.
But note that your code is unnecessarily complicated. You can simply use
Stream.<Fruit>concat(apples.stream(), oranges.stream())
.map(Fruit::getPickingMonth) // no more exception on this line
.forEachOrdered(System.out::println);
to achieve the same.
I believe you are just trying to reference a method from the interface with no return.
ie:
Fruit::getPickingMonth; //cant return anything
I would imagine you would want something like
Apple::getPickingMonth;
or
Orange::getPickingMonth;
Instead
If the above isn't the solution it might be a casting issue where the compiler doesn't know what to return on the bytecode level.
There are questions like this on StackOverflow
Lambda Referencing
Lambda Conversion Exception
I stumbled upon an issue when working through some old code, replacing several anonymous classes with either lambda expressions or method references. The problem is a bit hard to explain with words, but I'll do my best, and I've also added a short example illustrating my problem to the best of my abilities below.
My example consists of...
A functional interface, GenericListener, which takes a type parameter V and has a single method "genericCallback(V genericValue)".
A class, CallbackProducer, which takes a type parameter T. This class also has a method to add a GenericListener with type Integer.
A Main class which creates CallbackProducers and adds GenericListeners to them.
When I run CallbackProducer's addIntegerListener method from Main's constructor, I get the compiler error: "incompatible types" whenever i avoid specifying the type of CallbackProducer's T.
The method addIntegerListener only uses GenericListener's V. As far as I know, it doesn't use CallbackProducer's T in any way.
I've put several calls to addIntegerListener + comments in Main's constructor, 3 of which cause compiler errors. But as far as I can see (and according to IntelliJ) all of them should be legal. If you comment out the 3 first calls to addIntegerListener the application will compile and run just fine.
Also, if CallbackProducer didn't use generics, and we removed the type parameter T completely, the 3 first calls to addIntegerListener would compile.
Is there a reason for this behavior? Am I misunderstanding something, or is this a weakness or bug in the java compiler? (I'm currently using java 1.8_51)
Thanks in advance for any clarification!
import javax.swing.*;
public class Main {
public static void main(final String[] args) {
SwingUtilities.invokeLater(Main::new);
}
public Main() {
// Compiler error, type of CallbackProducer's "T" not specified
CallbackProducer producer1 = new CallbackProducer();
producer1.addIntegerListener(this::integerReceived);
// Compiler error, no diamond brackets for CallbackProducer
new CallbackProducer().addIntegerListener(this::integerReceived);
// Also compiler error for lambdas with no diamond brackets on CallbackProducer
new CallbackProducer().addIntegerListener(intValue -> integerReceived(intValue));
// Works because a (any) type for CallbackProducer's "T" is specified
CallbackProducer<Object> producer2 = new CallbackProducer<>();
producer2.addIntegerListener(this::integerReceived);
// Works because of the diamond brackets
new CallbackProducer<>().addIntegerListener(this::integerReceived);
// Lambda also works with diamond brackets
new CallbackProducer<>().addIntegerListener(intValue -> integerReceived(intValue));
// This variant also works without specifying CallbackProducer's "T"
// ... but it is a workaround I'd prefer to avoid if possible :-P
GenericListener<Integer> integerListener = this::integerReceived;
new CallbackProducer().addIntegerListener(integerListener);
}
private void integerReceived(Integer intValue) {
System.out.println("Integer callback received: " + intValue);
}
// A callback producer taking generic listeners
// Has a type parameter "T" which is completely unrelated to
// GenericListener's "V" and not used for anything in this
// example really, except help provoking the compiler error
public class CallbackProducer<T> {
// Adds a listener which specifically takes an Integer type as argument
public void addIntegerListener(GenericListener<Integer> integerListener) {
// Just a dummy callback to receive some output
integerListener.genericCallback(100);
}
}
// A simple, generic listener interface that can take a value of any type
// Has a type parameter "V" which is used to specify the value type of the callback
// "V" is completely unrelated to CallbackProducer's "T"
#FunctionalInterface
public interface GenericListener<V> {
void genericCallback(V genericValue);
}
}
Here's a shortened down version without all the comment clutter and with only two calls to "addIntegerListener", one of which causes compiler error.
import javax.swing.*;
public class Main {
public static void main(final String[] args) {
SwingUtilities.invokeLater(Main::new);
}
public Main() {
CallbackProducer producer1 = new CallbackProducer();
producer1.addIntegerListener(this::integerReceived); // Compiler error
CallbackProducer<Object> producer2 = new CallbackProducer<>();
producer2.addIntegerListener(this::integerReceived); // Compiles OK
}
private void integerReceived(Integer intValue) {
System.out.println("Integer callback received: " + intValue);
}
public class CallbackProducer<T> {
public void addIntegerListener(GenericListener<Integer> integerListener) {
integerListener.genericCallback(100);
}
}
#FunctionalInterface
public interface GenericListener<V> {
void genericCallback(V genericValue);
}
}
All 3 compiler errors are due to the fact that you are using a raw CallbackProducer. When you use a raw CallbackProducer, all type arguments undergo type erasure, such that any T, such as yours, without any upper bound, becomes Object.
Because of this, the addIntegerListener method expects a raw GenericListener as a parameter, something that integerReceived no longer fits. The integerReceived method takes an Integer, not an Object, as a raw GenericListener would supply.
You must supply the angle brackets <> on CallbackProducer to avoid using raw types, as you've done on your subsequent examples.
In Objective-C, instancetype can be used as the return type of methods that return an instance of the class they are called on (or a subclass of that class).
What is the equivalent of instancetype in Java?
The closest to thing is to use generics
interface Base<B extends Base<B>> {
// do something and return this.
B append(String s);
}
interface SubBase<B extends SubBase<B>> extends Base<SubBase<B>> {
// append returns a SubBase<B>
}
class MyClass implements SubBase<MyClass> {
public MyClass append(String s) {
// do something
return this;
}
}
It's not so elegant, but it works.
If you mean something like this:
class Vehicle {
instancetype doNothing() {return this;}
}
class Car extends Vehicle {}
Car c = new Car().doNothing(); // no compile errors here
There is no equivalent.
One workaround that's sometimes used is to pass the type as a generic parameter:
class Vehicle<instancetype> {
instancetype doNothing() {return this;}
}
class Car extends Vehicle<Car> {}
Car c = new Car().doNothing(); // works
but then you can't use the type Vehicle without getting warnings everywhere (it has to be Vehicle<?>).
I'm no Javaist but likely there is no equivalent, because Java needs it only rarely. (And Objective-C do so, too.)
Please note that in Objective-C you do not need it most of the time, too. This is, because the compiler infers the "real" return type from the receiver, even it is declared id. This applies to -init…, +alloc…, -new…:
To determine whether a method has an inferred related result type, the first word in the camel-case selector (e.g., “init” in “initWithObjects”) is considered, and the method will have a related result type if its return type is compatible with the type of its class and if:
the first word is “alloc” or “new”, and the method is a class method, or
the first word is “autorelease”, “init”, “retain”, or “self”, and the method is an instance method.
In Java this is inferred for the new operator, too. The constructors does not have any return type, so it does not need to be inferred.
Up to here: No need for an (explicit) keyword in Java or Objective-C.
In some cases you have additional methods returning a new instance of the receivers class. Cloning/copying is such a case. In Java you have to type cast the result. In Objective-C you can use instancetype to make code easier to read. (BTW: NSObject's -copy has id as return type, not instancetype. But again usually this is no problem, because you typically assign the return value to a typed reference.)
So the short conclusion: In both Java and Objective-C the return type can be and is inferred by the compiler for most of the use cases. In rare use cases you have to explicitly cast in Java.
I'm trying to switch a project to Java8, and encounter odd differences between Eclipse Luna and javac's type inference. With JDK 1.7.0_65 javac this code compiles just fine. JDK 1.8.0_11 complains that both toString(char[]) and toString(Throwable) match for the "toString(getKey(code, null));" line. Eclipse Luna 4.4 (I20140606-1215) compiles it happily with either JDK:
public class TypeInferenceTest {
public static String toString(Object obj) {
return "";
}
public static String toString(char[] ca) {
return "";
}
public static String toString(Throwable t) {
return "";
}
public static <U> U getKey(Object code, U defaultValue) {
return defaultValue;
}
public static void test() {
Object code = "test";
toString(getKey(code, null));
}
}
I think the only signature that could possibly match is toString(Object).
Of course I could simply add a cast to Object, but I wonder why javac can't infere the type by itself (while eclipse does), and why the heck javac considers Throwable and char[] suitable matches, but not Object.
Is this a bug in Eclipse or javac? (I mean only one compiler can be right here, either it compiles or it doesn't)
Edit: Error message from javac (JDK8):
C:\XXXX\Workspace\XXXX\src>javac -cp . TypeInferenceTest.java
TypeInferenceTest.java:22: error: reference to toString is ambiguous
toString(getKey(code, null));
^
both method toString(char[]) in TypeInferenceTest and method toString(Throwable) in TypeInferenceTest match
1 error
Compilers can only inspect the method signatures, not the method body, so that part is irrelevant.
This "reduces" your code to (psuedocode):
public class TypeInferenceTest {
public static String toString(Object obj);
public static String toString(char[] ca);
public static String toString(Throwable t);
public static <U> U getKey(Object code, U defaultValue);
public static void test() {
Object code = "test";
toString(getKey(code, null));
}
}
Also note that the <U> U getKey(...) really is: <U extends Object> U getKey(...).
All it knows that getKey(code, null) returns is: ? extends Object, so it returns a subtype of Object, or an Object itself.
There are three signatures that match, namely Object, char[] and Throwable, where both char[] and Throwable match equally and better than Object, because you asked for an ? extends Object.
So it cannot choose which is the correct one, because all three match the signature.
When you change it to:
public static Object getKey(Object code, Object defaultValue);
then only public static String toString(Object obj); matches, because it matches better as any other ? extends Object that is not equal to Object.
Edit, I looked over the original intent of the question: Why does it compile in Java 7, but not in Java 8?
In Java 8 type inference got greatly improved.
Whereas in Java 7 it could for example only infer that getKey returned an Object, it now in Java 8 infers that it returns an ? extends Object.
When using Java 7 there was only one match, namely Object.
To have the change visualized even better, consider this piece of code:
public class TypeInferenceTest {
public static String toString(Object obj) { return "1"; }
public static String toString(Throwable t) { return "2"; }
public static <U> U getKey(Object code, U defaultValue) { return defaultValue; }
public static void test() {
Object code = "test";
String result = toString(getKey(code, null));
System.out.println(result);
}
public static void main(String[] args) {
test();
}
}
On Java 7 it prints 1, on Java 8 it prints 2, exactly because of the reasons I have outlined above.
javac may actually be correct. The spec writes:
The null type has one value, the null reference, represented by the null literal null, which is formed from ASCII characters.
Therefore, the type of null is the null type.
The expression getKey(code, null) is a method invocation expression of a generic method. The spec defines its type as follows:
If the chosen method is generic and the method invocation does not provide explicit type arguments, the invocation type is inferred as specified in §18.5.2.
The actual description of the type inference algorithm is rather involved, but the type inferred for U must be assignable from the null type. Alas, this is true for all reference types, so which one to choose? The most logical one is the most specific such type, which is the null type. Therefore, the type of the method invocation expression probably is the null type.
Now, which method does the method invocation expression toString(getKey(code, null)) refer to? The spec writes:
The second step searches the type determined in the previous step for member methods. This step uses the name of the method and the argument expressions to locate methods that are both accessible and applicable, that is, declarations that can be correctly invoked on the given arguments.
There may be more than one such method, in which case the most specific one is chosen. The descriptor (signature plus return type) of the most specific method is the one used at run time to perform the method dispatch.
Since the type of the argument is the null type, all three toString methods are applicable. The spec writes:
A method is said to be maximally specific for a method invocation if it is accessible and applicable and there is no other method that is applicable and accessible that is strictly more specific.
If there is exactly one maximally specific method, then that method is in fact the most specific method; it is necessarily more specific than any other accessible method that is applicable. It is then subjected to some further compile-time checks as specified in §15.12.3.
It is possible that no method is the most specific, because there are two or more methods that are maximally specific. In this case:
If all the maximally specific methods have override-equivalent signatures (§8.4.2), then:
If exactly one of the maximally specific methods is concrete (that is, non-abstract or default), it is the most specific method.
Otherwise, if all the maximally specific methods are abstract or default, and the signatures of all of the maximally specific methods have the same erasure (§4.6), then the most specific method is chosen arbitrarily among the subset of the maximally specific methods that have the most specific return type.
In this case, the most specific method is considered to be abstract. Also, the most specific method is considered to throw a checked exception if and only if that exception or its erasure is declared in the throws clauses of each of the maximally specific methods.
Otherwise, the method invocation is ambiguous, and a compile-time error occurs.
Both toString(char[]) and toString(Throwable) are more specific that toString(Object), but neither is more specific than the other, nor are their signatures override-equivalent.
Therefore, the method invocation is ambiguous, and rejected by the compiler.