Bounded Type Parameters casting issue - java

I have a java interface like this
public interface MyInterface<T> {
public <V extends T> V get(String key, Bundle bundle);
}
please note the <V extends T> type parameter of the method.
Then I have a class MyFoo implements MyInterface
class MyFoo implements MyInterface<Object> { // Object because can be any type
#Override public <V> V get(String key, Bundle bundle) {
return new Other();
}
}
So when I now have a class like this:
class Bar {
public Other other;
public Other setOther(Other other);
}
Then I want to take MyFoo to set Other in a Bar instance:
MyFoo foo = new MyFoo();
Bar bar = new Bar();
bar.other = foo.get();
This works perfectly. Type can be determined by java generics. No additional cast is needed.
However, if I want to use bar.setOther() then the type can not be determined:
MyFoo foo = new MyFoo();
Bar bar = new Bar();
bar.setOther(foo.get()); // Fails
Then I get the following compile error:
error: incompatible types: Object cannot be converted to Other
I don't understand why this doesn't work for the bar.setOther( foo.get() ) method but works when accessing the field directly bar.other = foo.get()
Any idea how to solve that without adding an extra cast like bar.setOther( (Other) foo.get() )

In an assignment, the Java compiler knows what return type to use for the method by looking at the type that it is being assigned to. So to answer your question
Any idea how to solve that without adding an extra cast like
bar.setOther( (Other) foo.get() ) ?
This is a way in which you can do that:
Other value = foo.get();
bar.setOther(value);
There is another way, which looks worse but still doesn't have a cast:
bar.setOther(foo.<Other>get());
And a third alternative: switch to Java 8; in Java 8 you can just do bar.setOther(foo.get());.
For Java 7, the rules for this type inference are specified in JLS section 15.12.2.8:
If any of the method's type arguments were not inferred from the types
of the actual arguments, they are now inferred as follows.
If the method result occurs in a context where it will be subject to
assignment conversion (§5.2) to a type S, then [...]
A method result is subject to assignment conversion if it is used in an assignment expression.
Otherwise, the unresolved type arguments are inferred by invoking the
procedure described in this section under the assumption that the
method result was assigned to a variable of type Object.
If the unresolved type argument is used in a method whose result is not used in an assignment expression, then the type argument is interpreted as if the method result was assigned to a variable of type Object. So this means in this case that if the result of the get() method is not assigned to a variable, then the return type of the get() method is taken to be Object.

Related

Generic parameter: only diamond operator seems to work

Background: the question came up in this answer (the first revision of the answer, to be exact). The code presented in this question is reduced to the bare minimum to explain the problem.
Suppose we have the following code:
public class Sample<T extends Sample<T>> {
public static Sample<? extends Sample<?>> get() {
return new Sample<>();
}
public static void main(String... args) {
Sample<? extends Sample<?>> sample = Sample.get();
}
}
It compiles without warning and executes fine. However, if one tries to somehow define the inferred type of return new Sample<>(); in get() explicitly the compiler complains.
Up until now, I was under the impression that the diamond operator is just some syntactic sugar to not write explicit types and thus could always be replaced with some explicit type. For the given example, I was not able to define any explicit type for the return value to make the code compile. Is it possible to explicitly define the generic type of the return value or is the diamond-operator needed in this case?
Below are some attempts I made to explicitly define the generic type of the returned value with the corresponding compiler errors.
return new Sample<Sample> results in:
Sample.java:6: error: type argument Sample is not within bounds of type-variable T
return new Sample<Sample>();
^
where T is a type-variable:
T extends Sample<T> declared in class Sample
Sample.java:6: error: incompatible types: Sample<Sample> cannot be converted to Sample<? extends Sample<?>>
return new Sample<Sample>();
^
return new Sample<Sample<?>> results in:
Sample.java:6: error: type argument Sample<?> is not within bounds of type-variable T
return new Sample<Sample<?>>();
^
where T is a type-variable:
T extends Sample<T> declared in class Sample
return new Sample<Sample<>>(); results in:
Sample.java:6: error: illegal start of type
return new Sample<Sample<>>();
^
The JLS simply says:
If the type argument list to the class is empty — the diamond form <> — the type arguments of the class are inferred.
So, is there some inferred X that will satisfy the solution? Yes.
Of course, for you to explicitly define such an X, you'd have to declare it:
public static <X extends Sample<X>> Sample<? extends Sample<?>> get() {
return new Sample<X>();
}
The explicit Sample<X> is compatible with the return type Sample<? extends Sample<?>>, so compiler is happy.
The fact that return type is a messed up Sample<? extends Sample<?>> is an entirely different story.
Instantiating Generics with Wildcards
There's a couple problems here, but before delving into them, let me address your actual question:
Is it possible to explicitly define the generic type of the return value or is the diamond-operator needed in this case?
It is not possible to explicitly instantiate a Sample<? extends Sample<?>> (or a Sample<?> for that matter). Wildcards may not be used as type arguments when instantiating a generic type, though they may be nested within type arguments. For example, while it is legal to instantiate an ArrayList<Sample<?>>, you cannot instantiate an ArrayList<?>.
The most obvious workaround would be to simply return some other concrete type that is assignable to Sample<?>. For example:
class Sample<T extends Sample<T>> {
static class X extends Sample<X> {}
public static Sample<? extends Sample<?>> get() {
return new X();
}
}
However, if you specifically want to return a generic instantiation of the Sample<> class containing wildcards, then you must rely on generic inference to work out the type arguments for you. There are a few ways to go about this, but it usually involves one of the following:
Using the diamond operator, as you are doing right now.
Delegating to a generic method that captures your wildcard with a type variable.
While you cannot include a wildcard directly in a generic instantiation, it's perfectly legal to include a type variable, and that's what makes option (2) possible. All we have to do is ensure that the type variable in the delegate method gets bound to a wildcard at the call site. Every mention of the type variable the method's signature and body then gets replaced with a reference to that wildcard. For example:
public class Sample<T extends Sample<T>> {
public static Sample<? extends Sample<?>> get() {
final Sample<?> s = get0();
return s;
}
private static <T extends Sample<T>> Sample<T> get0() {
return new Sample<T>();
}
}
Here, the return type of Sample<T> get0() gets expanded to Sample<WC#1 extends Sample<WC#1>>, where WC#1 represents a captured copy of the wildcard inferred from the assignment target in Sample<?> s = get0().
Multiple Wildcards in a Type Signature
Now, let's address that method signature of yours. It's hard to tell for sure based on what code you've provided, but I would guess that a return type of Sample<? extends Sample<?>> is *not* what you really want. When wildcards appear in a type, each wildcard is distinct from all others. There is no enforcement that the first wildcard and second wildcard refer to the same type.
Let's say get() returns a value of type X. If it was your intention to ensure that X extends Sample<X>, then you have failed. Consider:
class Sample<T extends Sample<T>> {
static class X extends Sample<X> {}
static class Y extends Sample<X> {}
public static Sample<? extends Sample<?>> get() {
return new Y();
}
public static void main(String... args) {
Sample<?> s = Sample.get(); // legal (!)
}
}
In main, variable s holds a value that is a Sample<X> and a Y, but not a Sample<Y>. Is that what you'd intended? If not, I suggest replacing the wildcard in your method signature with a type variable, then letting the caller decide the type argument:
class Sample<T extends Sample<T>> {
static class X extends Sample<X> {}
static class Y extends Sample<X> {}
public static <T extends Sample<T>> Sample<T> get() { /* ... */ }
public static void main(String... args) {
Sample<X> x = Sample.get(); // legal
Sample<Y> y = Sample.get(); // NOT legal
Sample<?> ww = Sample.get(); // legal
Sample<?> wx = Sample.<X>get(); // legal
Sample<?> wy = Sample.<Y>get(); // NOT legal
}
}
The version above effectively guarantees that, for some return value of type A, the returned value extends Sample<A>. In theory, it even works when T is bound to a wildcard. Why? It goes back to wildcard capture:
In your original get method, the two wildcards could end up referring to different types. In effect, your return type was Sample<WC#1 extends Sample<WC#2>, where WC#1 and WC#2 are separate wildcards that are not related in any way. But in the example above, binding T to a wildcard captures it, allowing the same wildcard to appear in more than one spot. Thus, when T is bound to a wildcard WC#1, the return type expands to Sample<WC#1 extends Sample<WC#1>. Remember, there is no way to express that type directly in Java: it can only be done by relying on type inference.
Now, I said this works with wildcards in theory. In practice, you probably won't be able to implement get in such a way that the generic constraints are runtime-enforceable. That's because of type erasure: the compiler can emit a classcast instruction to verify that the returned value is, for example, both an X and a Sample, but it cannot verify that it's actually a Sample<X>, because all generic forms of Sample have the same runtime type. For concrete type arguments, the compiler can usually prevent suspect code from compiling, but when you throw wildcards into the mix, complex generic constraints become difficult or impossible to enforce. Buyer beware :).
Aside
If all this is confusing to you, don't fret: wildcards and wildcard capture are among the most difficult aspects of Java generics to understand. It's also not clear whether understanding these will actually help you with your immediate goal. If you have an API in mind, it might be best to submit it to the Code Review stack exchange and see what kind of feedback you get.

Discrepancy in Generic parametric polymorphism

public class Foo<T extends Bar>{
private Class<T> _type;
public Foo( Class<T> _type ){
this._type = _type;
}
public Collection<T> hypothetical( List<T> items ){ //PROBLEMATIC
return dostuffWithItems( items );
}
}
Usage:
Foo<? extends ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
List<ChildBar> items = ( List<ChildBar> ) foo.hypothetical( new ArrayList<ChildBar>() ); //COMPILER ERROR: The method hypothetical(List<capture#2-of ?>) in the type Foo<capture#2-of ?> is not applicable for the arguments (List<ChildBar>)
The compiler would either accept
casting List<ChildBar> items argument to List<?>
or changing the hypothetical( List<T> items ) signature to either
a) hypothetical( List<ChildBar> items ) or
b) hypothetical( List<? extends Bar> items )
However, none of the alternatives assure that the hypothetical method's List items argument T type is the equivalent runtime type of the Foo class T parametric type. I am currently using an extra method to verify the parametric types at the moment.
Is there a better way within Java generics constructs to achieve this automatically without the extra logic? Or better yet, why can I not declare foo as Foo<? extends Bar> and then fill in the actual type parameter at runtime?
I edited your code and added the missing stuff to make it compilable, and I can confirm that the only problematic parts are:
The missing dostuffWithItems method.
The typos with the hypothetical method name.
Assigning a Collection<ChildBar> to a List<ChildBar>.
The first two are easy to fix.
The last one requires you to either change the change the API method, or change the code where you are calling it. Neither of these is (IMO) problematic. Furthermore, the
It is worth noting that you would get all of these errors if the types were non-generic. You can't assign a Collection to a List without a typecast.
Here's my code for you to play with. (Copy and paste into appropriately named files ...)
public class Bar {
}
public class ChildBar extends Bar {
}
import java.util.*;
public class Foo<T extends Bar> {
private Class<T> _type;
public Foo( Class<T> _type ) {
this._type = _type;
}
public Collection<T> hypothetical( List<T> items ) {
return items; // dummy implementation ...
}
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Foo<ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
Collection<ChildBar> items =
foo.hypothetical( new ArrayList<ChildBar>() );
}
}
The accepted answer doesn't precisely explain why the snippet in the question (after edits) is rejected by the compiler.
We start from the observation that the snippet from #Stephen C's answer is accepted, while revision 8 of the question is rejected. The difference is: in the latter version the variable foo is declared with a wildcard-parameterized type Foo<? extends ChildBar>, while Stephen C had copied Foo<ChildBar> from an earlier revision (we all seem to agree that this is a suitable way to resolve the compile error).
To understand why this difference is crucial please see that with Foo<? extends ChildBar> foo this wildcard propagates as a capture into the signature for the invocation of foo.hypothetical, so this invocation is rendered as hypothetical(List<capture#2-of ?>), meaning that the parameter has an unknown (upper-bounded) type parameter. List<ChildBar> is not compatible to that type, hence the compile error.
Also note that all mentions of "runtime" in this thread are inappropriate, all this is statically resolved at compile time. Perhaps you meant invocation type, or type of the actual argument, as opposed to the declared type (of the formal parameter). The actual runtime type is unknown to the compiler.
This seems to be currently impossible in Java.
Foo<? extends ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
This leave foo with an ambiguous parametric type. It is obvious that ChildBar would become the true de facto parametric type. The call to the foo.hypothetical() method with the List<ChildBar> exposes this assumption to be untrue. Although foo.hypothetical only accepts a List<> argument containing elements of the foo parametric type, it still fails to recognize that the argument was a list of ChildBar objects.
For this use case, the object parametric type must be specified during foo declaration in order make it a part and parcel of the foo runtime reference.
Foo<ChildBar> foo = new Foo<ChildBar>( ChildBar.class );
All conforming List<ChildBar> arguments of the foo.hypothetical method will now correctly be accepted as carrying elements of the foo's declared parametric type.

Upper bounded generics VS superclass as method parameters?

As far as I know, using an upper bounded generic and using a superclass as a method parameter both accept the same possible arguments. Which is preferred, and what's the difference between the two, if any?
Upper bounded generic as parameter:
public <T extends Foo> void doSomething(T foo) {}
Superclass as parameter:
public void doSomething(Foo foo) {}
That's an upper bounded type parameter. Lower bounds are created using super, which you can't really do for a type parameter. You can't have a lower bounded type parameter.
And that would make a difference, if you, for example want to pass a List<T>. So, for the below two methods:
public <T extends Foo> void doSomething(List<T> foos) {}
public void doSomething(List<Foo> foo) {}
And for the given class:
class Bar extends Foo { }
The following method invocation:
List<Bar> list = new ArrayList<Bar>();
doSomething(list);
is valid for 1st method, but not for 2nd method. 2nd method fails because a List<Foo> is not a super type of List<Bar>, although Foo is super type of Bar. However, 1st method passes, because there the type parameter T will be inferred as Bar.
Generally, you only need a type variable when it's used in more than one place in class/method/field declarations. When you declare one on a method (rather than a class), the only places to use it are on the parameters and return value of that method.
For example, you can use it on multiple parameters to ensure their types match:
public static <T> void addToList(List<T> list, T element) {
list.add(element);
}
This is a trivial example, but you can see that it prevents you from giving it an element that doesn't match the list's generic type:
List<Integer> list = new ArrayList<>();
addToList(list, 7);
//addToList(list, 0.7); // doesn't compile
//addToList(list, "a"); // doesn't compile
You can also declare a parameter and the return type to be the same type:
public static <T> T nullCheck(T value, T defValue) {
return value != null ? value : defValue;
}
Since this method is returning one of the two T objects it's given, we can safely say that the returned object is also of type T.
Integer iN = null;
Integer i = nullCheck(iN, 7);
System.out.println(i); // "7"
Double dN = null;
Double d = nullCheck(dN, 0.7);
System.out.println(d); // "0.7"
Number n = nullCheck(i, d); // T = superclass of Integer and Double
System.out.println(n); // "7"
As for the example in the question, the type variable is only being used once, so it's equivalent to using the superclass. In this case you should avoid declaring a type variable, it's just unnecessary clutter.
Also I should note that the other answer changes the example to use List<T> and List<Foo>, but as mentioned in the comments, the superclass is really List<? extends Foo>, so no type variable is needed there, either.

Why doesn't Class have a nice generic type in this case?

In this code, why can type not be declared as Class<? extends B>?
public class Foo<B> {
public void doSomething(B argument) {
Class<? extends Object> type = argument.getClass();
}
}
This problem is that Java's syntax doesn't allow getClass() to say it's returning a type which matches the class its defined in, and this is not a special case as far as the compiler is concerned. So you are forced to cast the result.
There are many cases where you would like to be able to specify this type, e.g. for chaining, so I would hope they include this feature one day.
You could write
Class<? extends this> getClass();
or
public this clone(); // must return a type of this class.
or
class ByteBuffer {
this order(ByteOrder order);
}
class MappedByteBuffer extends ByteBuffer {
}
// currently this won't work as ByteBuffer defines order()
MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, fc.size())
.order(ByteOrder.nativeOrder());
This is all about generic type erasure. From here:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods. [at compile time]
So you cannot get Class of actual type of B but only ? or ? extends Object.
If your bounds will be turn into <B extends SomeClass> instead of <B> only then you can fetch Class object of type <? extends SomeClass>.
Object.getClass() is defined to return a Class, where T is the statically known type of the receiver (the object getClass() is called on). Take special note of the vertical bars, the erasure operator. The erasure of a type variable is the erasure of its leftmost bound. In your case that's the implicit bound Object. So you get back a Class, not a Class<? extends T>.
The right way to do it is,
abstract class AbstractExecutor<E> {
public void execute() throws Exception {
List<E> list = new ArrayList<E>();
Class<E> cls = (Class<E>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
E e = cls.getConstructor(String.class).newInstance("Gate");
list.add(e);
System.out.println(format(list));
}
// ...
}
Because the class of a given object is not guaranteed to be the same as the type it is stored in.
eg.
Object o = "some string";
Class<Object> clazz = o.getClass(); // actually Class<String>
By looking at the type you should expect the Class for Object, but in reality you get the class for String. What problem is this you might ask -- Object is a superclass of String, so String implements all the methods implemented by Object.
Problems
The problems are that when getting a Field the class will return the fields of the actual class and not the generic type. In addition, whilst Method is able to invoke the correct method if there is an overriding method in the given object, it is not able to do the reverse and find an implementation of the method that will work on the given object.
For instance, Object declares hashCode, so all objects have a hash code method. However, the following will produce a runtime exception:
Object.class.getMethod("hashCode").invoke("some string"); // works
String.class.getMethod("hashCode").invoke(new Object()); // fails
This is because the Method object for hashCode is expecting a String. It's expecting to generate a hash code from the sequence of characters, but the provided object does not have a char array for the method to work on, so it fails.
Meaning the following looks like it should work, but won't because the actual method returned by getMethod is the hash code method for String.
Object obj = "string";
Class<Object> clazz = obj.getClass();
clazz.getMethod("hashCode").invoke("another string");

Java generics T vs Object

I was wondering what is the difference between the following two method declarations:
public Object doSomething(Object obj) {....}
public <T> T doSomething(T t) {....}
Is there something you can/would do with one but not the other? I could not find this question elsewhere on this site.
Isolated from context - no difference. On both t and obj you can invoke only the methods of Object.
But with context - if you have a generic class:
MyClass<Foo> my = new MyClass<Foo>();
Foo foo = new Foo();
Then:
Foo newFoo = my.doSomething(foo);
Same code with object
Foo newFoo = (Foo) my.doSomething(foo);
Two advantages:
no need of casting (the compiler hides this from you)
compile time safety that works. If the Object version is used, you won't be sure that the method always returns Foo. If it returns Bar, you'll have a ClassCastException, at runtime.
The difference here is that in the first, we specify that the caller must pass an Object instance (any class), and it will get back another Object (any class, not necessarily of the same type).
In the second, the type returned will be the same type as that given when the class was defined.
Example ex = new Example<Integer>();
Here we specify what type T will be which allows us to enforce more constraints on a class or method. For example we can instantiate a LinkedList<Integer> or LinkedList<Example> and we know that when we call one of these methods, we'll get back an Integer or Example instance.
The main goal here is that the calling code can specify what type of objects a class will operate upon, instead of relying on type-casting to enforce this.
See Java Generics* from Oracle.
*Updated Link.
The difference is that with generic methods I don't need to cast and I get a compilation error when I do wrong:
public class App {
public static void main(String[] args) {
String s = process("vv");
String b = process(new Object()); // Compilation error
}
public static <T> T process(T val) {
return val;
}
}
Using object I always need to cast and I don't get any errors when I do wrong:
public class App {
public static void main(String[] args) {
String s = (String)process("vv");
String b = (String)process(new Object());
}
public static Object process(Object val) {
return val;
}
}
You don't need to do additional class casting. In first case you will always get an object of class java.lang.Object which you will need to cast to your class. In second case T will be replaced with the class defined in generic signature and no class casting will be needed.
At runtime, nothing. But at compile time the second will do type checking to make sure the type of the parameter and the type of the return value match (or are subtypes of) whatever type T resolves to (the first example also does type checking but every object is a subtype of Object so every type will be accepted).
T is a generic type. Meaning it can be substituted by any qualifying object at runtime. You may invoke such a method as follows:
String response = doSomething("hello world");
OR
MyObject response = doSomething(new MyObject());
OR
Integer response = doSomething(31);
As you can see, there is polymorphism here.
But if it is declared to return Object, you can't do this unless you type cast things.
in the first case it takes a parameter of any type e.g.string and return a type foo. In the second case it takes a parameter of type foo and returns an object of type foo.
There are few reasons that you can consider Generics over Object type in Java:
Generics is flexible and safe. At the same time, working with Object that requires type-casting is error-prone
Type Casting in Java is slow
ref : [1]: https://www.infoworld.com/article/2076555/java-performance-programming--part-2--the-cost-of-casting.html

Categories

Resources