Why new Base() cannot be passed to <? extends Base>? - java

This code:
public class Base<E> {
static void main(String[] args) {
Base<? extends Base> compound = new Base<Base>();
compound.method(new Base());
} // ^ error
void method(E e) { }
}
Gives such compilation error:
Error:(4, 17) java: method method in class Base<E> cannot be applied to given types;
required: capture#1 of ? extends Base
found: Base
reason: actual argument Base cannot be converted to capture#1 of ? extends Base by method invocation conversion
From what I understand, E becomes ? extends Base, something that extends Base. So, why new Base() can't be passed?

Base<? extends Base> compound means that compound is parameterized with some subtype of Base, but you don't know which one. If the parameter type is unknown, then the compiler can't check that new Base() matches that type.
E does become ? extends Base You might think that the method would accept any subtype of Base, but it doesn't. It only accepts one specific but unknown subtype of Base.
So you can't call any method that takes E as a parameter, but you can call a method that returns E.
Allowing your example to compile would lead to type-safety errors like:
List<? extends Object> list = new ArrayList<String>();
list.add(new Object()); // Error - Can't add object to list of Strings.

Try the following to define your method as a generic:
<E> void method(E e) { }
Note the the addition of the <E>.
This fixed the compilation error for me. Although, I would have expected the parameter you supplied at class-level to have applied.

Related

Why does my generic function return different captures as it gets?

I have a superclass with a generic type extending a supertype (<E extends ...>). This class has an abstract method that returns a list of the generic type.
Also I have a few subclasses implementing the abstract method.
When I call this method and try to replace objects in the list, the java compiler shows an error. I think the error is because my converting function returns a different capture of the type as it gets as parameter.
Here is a sample code using Exception as generic supertype:
import java.util.ArrayList;
import java.util.List;
public class GenericTest {
abstract class Super<E extends Exception>{
abstract List<E> foo();
}
class Sub1 extends Super<NullPointerException>{
#Override
List<NullPointerException> foo(){
return new ArrayList<NullPointerException>();
}
}
GenericTest(Super<? extends Exception> s){
List<? extends Exception> list = s.foo();
list.set(0, convertException(list.get(0)));
}
static <F extends Exception> F convertException(F exception){...}
}
There are two error occurs in the line
list.set(0, convertException(list.get(0)));
The compiler says for set:
The method set(int, capture#2-of ? extends Exception) in the type List<capture#2-of ? extends Exception> is not applicable for the arguments (int, capture#3-of ? extends Exception)
and for convertException:
Type mismatch: cannot convert from capture#3-of ? extends Exception to capture#2-of ? extends Exception
Why doesn't convertEException return the same capture#x as it gets? It takes F and returns F.
Thanks for your help in advance!
This is because you are passing Super to the constructor as a raw type. You are not using the generic. Since you don't specify the generic type, the compiler considers the list as a list of Object.
It should be like this:
GenericTest(Super<Exception> s){
Exception e = s.foo().get(0);
}
This will compile fine
UPDATE
The compiler says for set
The method set(int, capture#2-of ? extends Exception) in the type List is not applicable for the arguments (int, capture#3-of ? extends Exception)
Java doesn't allow you to add or update elements of a Collection when you're using wildcard. So, using:
List<? extends Exception> list = s.foo();
list.set(0, /*anything but null*/);
is forbidden.
The reason is to avoid this situation:
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
public void method() {
List<Dog> dogs = new ArrayList<Dog>();
addCats(dogs);
}
void addCats(List<? extends Animal> list) {
list.add(new Cat());
}
You see the point? If adding operation were allowed, you would risk to add cats in an list of dogs.
Back to your problem. I don't understand precisely what you have to do, if you really need a list with a specific subtype of exception, I suggest you to make also GenericTest as a generic class . Otherwise you can declare your list as a simple list of Exception:
GenericTest(Super<Exception> s){
List<Exception> list = s.foo();
list.set(0, convertException(list.get(0)));
}
and then make instanceof checks on your list elements.
Hope this helps
UPDATE 2
Why does convertException not know, that it will return the same type as the list has?
The problem is not that the compiler doesn't know that "F extends Exception == ? extends Exception". This piece of code:
GenericTest(Super<Exception> s){
List<? extends Exception> list = getList();
}
<F extends Exception> List<F> getList(){...}
will compile. The problem is that you're using the set method on collections with wildcard, wich is forbidden, no matter what object are you actually passing.
You can use casting. Object is a superclass https://docs.oracle.com/javase/tutorial/java/IandI/objectclass.html
Exception e = (Exception) s.foo().get(0);
I think this is what you were aiming for?
List<NullPointerException> ds = new GenericTest.Sub1().foo();
Exception e2 = ds.get(0);

Class<Result> cannot be converted to Class<Result<Integer>>

Suppose I have and API method declared as following
public class Dummy {
public static class Result<ValueT extends Comparable> {
public ValueT value;
}
public static <ValueT extends Comparable> Result<ValueT>
getResult(Class<? extends Result<ValueT>> ofType) throws Exception {
return ofType.newInstance();
}
}
Now I'd like to invoke it relying on Java compile-time type verification and just can't find correct syntax to do this:
Attempts:
getResult(Result<Integer>.class) <-- expected syntax
public static void invokeGetResult() {
Result<Integer> intResult = getResult(Result<Integer>.class);
}
results in
error: <identifier> expected
Result<Integer> intResult = getResult(Result<Integer>.class);
^
getResult(Result.class) <-- just to try
public static void invokeGetResult() {
Result<Integer> intResult = getResult(Result.class);
}
results in
error: method getResult in class Dummy cannot be applied to given types;
Result<Integer> intResult = getResult(Result.class);
^
required: Class<? extends Result<ValueT>>
found: Class<Result>
reason: cannot infer type-variable(s) ValueT
(argument mismatch; Class<Result> cannot be converted to Class<? extends Result<ValueT>>)
where ValueT is a type-variable:
ValueT extends Comparable declared in method <ValueT>getResult(Class<? extends Result<ValueT>>)
getResult((Class<Result<Integer>>)Result.class) <-- just to try
public static void invokeGetResult() {
Result<Integer> intResult = getResult((Class<Result<Integer>>)Result.class);
}
results in
error: incompatible types: Class<Result> cannot be converted to Class<Result<Integer>>
Result<Integer> intResult = getResult((Class<Result<Integer>>)Result.class);
^
If the only purpose of passing the Class<...> is to instantiate it, consider using a Supplier<...> instead. It does not have this problem, and does not throw any exceptions.
Here is an example:
class SomeClass extends Result<Integer> {...}
public static <T extends Result<?>> T getResult(Supplier<T> constructor) {
return constructor.get();
}
Use like:
getResult(SomeClass::new); // Passes the constructor.
There isn't really such thing as a Class<Result<Integer>>. Class objects represent reifiable classes at runtime, and there is only one single Class object at runtime for the class Result, which you can get via the expression Result.class, which has type Class<Result>. There are no Class objects representing Result<Integer> or Result<String>, etc.
You can take the expression of type Class<Result> (which you get from Result.class) and do a bunch of unsafe casts on it to turn it into a Class<Result<Integer>> or something like that, for example:
`(Class<Result<Integer>>)(Class<?>)Result.class`
but not that this is unsafe. It is unsafe because the interface of Class has certain methods that return T (the type parameter of the class Class) based on runtime type operations with the class. For example, Class has a method .cast() which checks whether the passed object is an instance of the class, and if not, throws an exception, and if it is, then returns the object, as type T. If you call Result.class.cast(), it returns a Result, and the method indeed checks at runtime that the object is a Result. But if you call .cast() on an expression of type Class<Result<Integer>>, it returns type Result<Integer>, but the class could not have checked at runtime that the thing is a Result<Integer>, because 1) this is just the same Result class object, and 2) generic information doesn't exist at runtime. So you would get a Result<Integer> that might not be a Result<Integer>.
So basically, you need to think about what you are using this class object to do, and how it makes sense to have it as a Class<Result<Integer>> when all you can have at runtime is a class object representing the raw type Result.
Here you have a method that takes Class<? extends Result<ValueT>>. I am wondering why you have the wildcard here. Are you trying to have it accept subclasses of Result? Because if not, then the only thing that can be passed in is the unique Result class object itself, which means the parameter is basically pointless because it's always the same thing that can be passed in; in that case you might as well get rid of the parameter and just do:
public static <ValueT extends Comparable<? super ValueT>> Result<ValueT>
getResult() throws Exception {
return new Result<ValueT>();
}
If you are going to accept subclasses of Result, then you're assuming that all subclasses must have a no-parameter constructor, as you are calling .newInstance() on it, which is bad design. You should use something like a Supplier as Jorn Vernee's answer suggests.
EDIT: I did not understand clearly the original intentions. This example should work for your call, however it is a bit hacky and not so recommended.
public class Dummy {
public static class Result<ValueT extends Comparable> {
public ValueT value;
}
public static <ValueT extends Comparable> Result<ValueT>
getResult(Class<? extends Result<ValueT>> ofType) {
return null;
}
}
You can call it like this:
Dummy.Result<Integer> result = Dummy.getResult(new Result<Integer>(){}.getClass());
The compile time verification should work with this example, as you are creating an anonymous empty class and it's type information can be retrieved successfully.
You could also do a type cast as #Jorn suggested in comments, although your compiler will warn you about the fact it is an "unchecked cast".

PECS does not work on return types with interface

Consider the following example,
class ClsA {}
class ClsB {}
interface IntA {}
interface IntB {}
And I have 2 very similar methods:
static <T extends ClsA> T returnC() { // Here T extends the class
return null;
}
static <T extends IntA> T returnI() { // Here T extends the interface
return null;
}
And then the method calls:
ClsA ac = returnC(); // This works fine based on inference.
IntA ai = returnI(); // Similarly this works fine based on inference.
But consider the below 2:
ClsB bc = returnC(); // ERROR as expected.
Eclipse Error:
Bound mismatch: The generic method returnC() of type Testing is not applicable for the arguments (). The inferred type ClsB&ClsA is not a valid substitute for the bounded parameter <T extends ClsA>
But the following code compiles fine:
IntB bi = returnI(); // Works fine
Why is that for interface, the generics bound is not considered in return types?
The magic words here are raws and multiple inheritance.
Lets first take a look on your returnC method:
static <T extends ClsA> T returnC() {
return null;
}
The type T is bounded with ClsA, which means that if you invoke the raw returnC method, then the return type will be simply ClsA.
True, when you have this statement: ClsA ac = returnC(); the compiler succeeds with the compilation, because the raw return type of the method is ClsA, which is compatible with the type of ac.
The the raw return type is also the reason for which the statement ClsB bc = returnC(); does not compile.
Now let's take a look on the returnI method:
static <T extends IntA> T returnI() { // Here T extends the interface
return null;
}
Here, the type parameter is bound to IntA only.
This, however, doesn't mean that the replacement type for T must implement only IntA - the type can implement IntA and IntB at the same time. Statements like IntB bi = returnI(); are allowed, because a type can implement multiple interfaces, but cannot implement multiple classes.
Consider this class:
class SomethingReallyCool implements IntA, IntB { }
This type is a valid substitute for the type-parameter of returnI() and the proof for that is this statement:
IntB bi = YourClass.<SomethingReallyCool>returnI();
Why? Because it's a class that implements IntA and this is the only thing the compiler cares about.

Bound mismatch warning in java generics

Please have a look at below code listing :-
package com.test;
class XParam implements Comparable<XParam>{
#Override
public int compareTo(XParam o) {
// TODO Auto-generated method stub
return 0;
}
}
public class Main {
public static void main(String[] args) {
Comparable<XParam> param = new XParam() ;
SomeClass object = new SomeClass() ;
object.setData( param );
}
}
Now,
object.setData( param ); //third statementt in the main method in above code listing
compiles fine, if i make SomeClass generic with formal type parameter
<T extends Comparable<T>>
as shown below :-
class SomeClass<T extends Comparable<T>>{
public void setData(T data) {
}
}
But, object.setData( param ); gives below compile time error in eclipse :-
Bound mismatch: The generic method setData(T) of type SomeClass is not applicable for the
arguments (Comparable<XParam>). The inferred type Comparable<XParam> is not a valid
substitute for the bounded parameter <T extends Comparable<T>>
if i apply the same formal type parameter at method level, rather then adding it at class level as shown below :-
class SomeClass{
public <T extends Comparable<T>>void setData(T data) {
}
}
Any good explanation/reference to understand this concept is highly appreciated.
Thanks.
I think the only reason you're getting a bound mismatch with one way and not the other is because it doesn't seem you are parametrizing the declaration of SomeClass, and are thus using a raw type. If you use a raw type, the compiler doesn't bother with any generic checks, so you get no complaints.
I think that if you attempted to genericize your class with Comparable<XParam> you would get the same error you got when you tried to call the generic method with an incorrect bound.
As for the generic method, the error makes sense.
If your bound is T extends Comparable<T>, and you pass the method a Comparable<XParam>, the compiler will infer T == Comparable<XParam> and the method will look to see if the argument extends Comparable<T> == Comparable<Comparable<XParam>>. Which it doesn't.
I believe that your method will work if you call the method with an argument of type XParam, as XParam extends Comparable<XParam>.

Curious type error in java generics

I've encountered a strange issue with the following code (well, not exactly this code):
public class CompilationProblems1 {
static Box<Class<? extends AlcoholicBewerage>> brokenBoxOfOneBeer = boxOf(Beer.class);
static Box<? extends Class<? extends AlcoholicBewerage>> boxOfOneBeer = boxOf(Beer.class);
static Box<Class<? extends AlcoholicBewerage>> boxOfBeerAndVodka = boxOf(Beer.class, Vodka.class);
interface AlcoholicBewerage {}
class Beer implements AlcoholicBewerage {}
class Vodka implements AlcoholicBewerage {}
static class Box<T> {}
static <E> Box<E> boxOf(E e) {
return new Box<E>();
}
static <E> Box<E> boxOf(E e1, E e2) {
return new Box<E>();
}
}
The first declaration brokenBoxOfOneBeer gives a compilation error:
found : lt.tool.CompilationProblems1.Box<java.lang.Class<lt.tool.CompilationProblems1.Beer>>
required: lt.tool.CompilationProblems1.Box<java.lang.Class<? extends lt.tool.CompilationProblems1.AlcoholicBewerage>>
static Box<Class<? extends AlcoholicBewerage>> brokenBoxOfOneBeer = boxOf(Beer.class);
This error happens on OpenJDK 6, Eclipse and IntelliJ. I understand that it's a limitation of the type inferencer.
In the third case (boxOfBeerAndVodka) the compiler is able to inference the correct covariant type because it has two subtypes to choose from, I believe. But why isnt' the compiler able to compile the first declaration, but is OK with the second one?
But why isnt' the compiler able to compile the first declaration, but is OK with the second one?
In both cases, the expression boxOf(Beer.class) has type Box<Class<Beer>>.
The first declaration needs it to have type Box<Class<? extends AlcoholicBewerage>>; since Box<Class<Beer>> isn't a subtype of Box<Class<? extends AlcoholicBewerage>>, this doesn't work. (Class<Beer> is a subtype of Class<? extends AlcoholicBewerage>, but due to invariance, this does not entail that Box<Class<Beer>> is a subtype of Box<Class<? extends AlcoholicBewerage>>.)
The second declaration needs it to have type Box<? extends Class<? extends AlcoholicBewerage>> — which it does. Class<Beer> is a subtype of Class<? extends AlcoholicBewerage>, ergo Box<Class<Beer>> is a subtype of Box<? extends Class<? extends AlcoholicBewerage>>.
That is, the exact same thing is happening in your declarations as would happen in:
List<Object> foo = new ArrayList<String>(); // doesn't work
List<? extends Object> bar = new ArrayList<String>(); // works
It just looks more complicated because instead of Object and String you have another level of generic types.
Type arguments are inferred based on actual arguments (Beer.class) first (§15.12.2.7). If a type argument could not be inferred based on actual arguments, the context (brokenBoxOfBeer) is taken into consideration (§15.12.2.8).
Thus, you can work around this issue as follows.
static <X, E extends X> Box<X> boxOf(E e) {
return new Box<X>();
}
Edit: I should note that this is a work-around and comes with its own set of problems. Nested invocations will no longer be inferred correctly.
Box<Box<Integer>> x = boxOf(boxOf(1)); // won't compile

Categories