Why does this method fail? (Generics and wildcards)

I get the following error:

'call(ContainsMonitor)' cannot invoke 'call(? extends webscout.Monitor)' in 'WebScoutCallable' 

Monitor.java

 WebScoutCallable<? extends Monitor> handler; public setCallable(WebScoutCallable<? extends Monitor> callable) { this.handler = callable; } 

WebScoutCallable.java

 public interface WebScoutCallable<T extends Monitor> { public void call(T caller); } 

ContainsMonitor.java

 public class ContainsMonitor extends Monitor { public void handleDocument() { handler.call(this); } } 

I will freely admit that I am new to generics and still new to Java itself. I find the error message confusing since it looks like it should work (the method declaration expects a monitor or subclass, I pass to the subclass). Any help (+ explanation) would be greatly appreciated!

Thanks!

+7
java generics extends
source share
3 answers

You have a wildcard in the type parameter of your handler variable. The compiler does not know what the exact type of this parameter is, only that it is either Monitor or a subclass.

The call method takes a T that maps to a wildcard. But there is no guarantee that the type of the wildcard is ContainsMonitor . It could be Monitor , or it could be MonitorSubtypeThatDoesntExistYet . Since the compiler does not know the actual type, it cannot skip anything but null , because with any argument not null it cannot guarantee type safety.

You can get around this by removing the wildcard and replacing the concept with a type parameter in the Monitor class.

 class Monitor<T extends Monitor<T>> { WebScoutCallable<T> handler; public void setCallable(WebScoutCallable<T> callable) { this.handler = callable; } } 

The WebScoutCallable interface WebScoutCallable slightly in response:

 interface WebScoutCallable<T extends Monitor<T>> { public void call(T caller); } 

A subclass substitutes its name as a type argument when extending Monitor .

 class ContainsMonitor extends Monitor<ContainsMonitor> { public void handleDocument() { handler.call(this); } } 

Now T will be a known type, and ContainsMonitor defines it as itself, so now it is legal to go to call .

+6
source share

? extends Monitor ? extends Monitor means: a specific subclass of Monitor, but we don’t know which one. Thus, it may or may not be a ContainsMonitor , and the handler may or may not accept a ContainsMonitor . The compiler cannot solve and shows an error.

One way to solve your problem is to use certain types, for example:

 class Monitor<T extends Monitor<T>> { WebScoutCallable<T> handler; public setCallable(WebScoutCallable<T> callable) { this.handler = callable; } } class ContainsMonitor extends Monitor<ContainsMonitor> { public void handleDocument() { handler.call(this); } } 
+3
source share

No generics are required in your code.

 public class Monitor { WebScoutCallable handler; public void setCallable(WebScoutCallable callable) { this.handler = callable; } } public interface WebScoutCallable { public void call(Monitor caller); } public class ContainsMonitor extends Monitor { public void handleDocument() { handler.call(this); } } 
-5
source share

All Articles