Java - generic types - java

I am trying to create a binary tree using generic typing and I have an error I do not understand. I tried two ways of coding and the second worked. I do not understand why the first was failing.
I have the following common code:
public class MyTreea <T extends Comparable<T>>
{
class BT_Node<T extends Comparable<T>>
{
T value;
BT_Node<T> left;
BT_Node<T> right;
BT_Node(T node_value)
{
this.value = node_value ;
left = null;
right = null;
}
}
The difference is in insert procedure:
This works:
private BT_Node<T> insert(BT_Node<T> node, BT_Node<T> newNode) {
if ((node.value).compareTo(newNode.value) == 0) { . . . }
But this fails
private BT_Node insert(BT_Node node, T value) {
if (value.compareTo(node.value) == 0) { . .
With:
MyTreea.java:28: error: method compareTo in interface Comparable<T#2> cannot be applied to given types;
if (value.compareTo(node.value) == 0)
required: T#1
found: Comparable
reason: actual argument Comparable cannot be converted to T#1 by method invocation conversion
where T#1,T#2 are type-variables:
T#1 extends Comparable<T#1> declared in class MyTreea
T#2 extends Object declared in interface Comparable
Obviously if I call BT_insert with two nodes parameters it tis fine. (And I code to cater for that by creating an extra function) but I would have preferred not to it this way.
Thanks

You should be able to use the second example with the following signature:
private BT_Node<T> insert(BT_Node<T> node, T value)
The example you gave uses "raw types" (i.e. a generic class without a generic parameter), which is generally always a bad idea and arguably only exists for backwards compatibility with Java 1.4. BT_Node is a generic class, so you should always give it a generic parameter.
In the case where you didn't, this was roughly equivalent to passing in BT_Node<?>, in that the Node could have had any value for its generic parameter. So the compiler could not guarantee that the value you were passing in was of the correct type - the first argument could be a BT_Node<Int> for example, while T is String.
By passing in the generic parameter as T, the compiler can check that the type of the value and the type of the node do match.

BT_Node can use T without parametrising it again, remove its T parameter.
Otherwise you have to different Ts which could be different extensions of their Comparable.
Also no BT_Node<T>.

Related

java - generic type ignored by a compiler

recently I've stumbled upon an edge case with generics that doesn't seem logical.
I've got a simple interface that returns a list of children:
public interface INode<C extends INode> {
List<C> getChildren();
}
I would expect that if the INode type is referenced without defining the C type, the C type would be inferred by a compiler as the INode. In other words, in below code:
public void retrieveChildren(INode node) {
var children = node.getChildren();
}
children would be inferred as of List<INode> type. Instead, it's just plain List.
Worth noting that type inference works as expected with the INode method returning a single element, so when this interface:
public interface INode<C extends INode> {
C getFirstChild();
}
is used in the following method:
public void retrieveFirstChild(INode node) {
var firstChild = node.getFirstChild();
}
firstChild is propely inferred as INode.
Is there a rationale for the jdk to work like that? Also, is there a clean way to enforce a C type for a returned list?
Many thanks.
children would be inferred as of List< INode> type. Instead, it's just plain List.
Once you use raw types, it infects everything. INode has typeargs, and in your signature (retrieveFirstChild), you use it raw, which means all interactions that involve any generics with that variable are also raw.
The solution is to not use raw, ever (the compiler warns, and you should heed these warnings!):
public void retrieveFirstChild(INode<?> node) {
// note the <?> up there!
var firstChild = node.getFirstChild();
}
now firstChild's type is INode, as expected.
First of all, what you really want is:
public interface INode<C extends INode<C>> {
List<C> getChildren();
C getFirstChild();
}
Since you are using a raw type here :
public void retrieveChildren(INode node) {
var children = node.getChildren();
}
you are going to apply erasure to INode, and the erasure of that is INode, i.e.: C extends INode is going to be erased to INode. That is explained in the JLS:
To facilitate interfacing with non-generic legacy code, it is possible to use as a type the erasure of a parameterized type or the erasure of an array type whose element type is a parameterized type. Such a type is called a raw type.
And that is visible because of two things:
INode first1 = node.getFirstChild(); // works
and if you de-compile the actual .class file (javap -c -p -v), you will see :
InterfaceMethod DeleteMe$INode.getFirstChild:()LDeleteMe$INode;
or in plain english getFirstChild returns an INode.
On the other hand List<C> getChildren(); since it itself uses generics, will be erased to List, without type arguments. So these will both compile, for example:
List<INode> children1 = node.getChildren();
List<String> children2 = node.getChildren();

Generics and compareTo() method

I am trying to make a SkipList and I have a method that takes a generic data type:
public void add(E key, Integer value)
{
Node<E> p;
p = find(key);
}
Which takes you here:
public Node<E> find(E key)
{
//Start at head
Node<E> p = head;
while (true)
{
while ( (p.getRight().getKey() != Node.posInf) && (p.getRight().getKey().compareTo(key) <= 0 ))
{
p.setRight(p.getRight());
}
//More stuff down here
}
}
The problem is on the compareTo() method. It says the compareTo() method is undefined for type E. In Eclipse it wants me to add two typecasts like this:
((String) p.getRight().getKey().compareTo((String) key) <= 0 )
Why does it want String? The data type could be anything. I tried doing typecast of E instead but Eclipse wants to change it back to String. Any help would be appreciated.
You haven't shown how E is defined, but the error message indicates that you didn't place an upper bound of Comparable<E> on the declaration of E.
You can accomplish that with something like this on your class:
public class SkipList<E extends Comparable<E>>
This will allow you to call compareTo on your key variable of type E.
As to why Eclipse is suggesting casting to a String, it looks like Eclipse is guessing as to what would be the best change to make to make it compile. It may have guessed String because it's Comparable<String>. In this case, it's wrong, because E isn't necessarily a String. The solution here is different, as I stated above: restrict E to be Comparable<E>.
The method compareTo is defined in the interface java.lang.Comparable. There is nothing in your code that tells the compiler that the type parameter E is Comparable. You can do this in the generic type declaration:
class Node<E extends Comparable<E>> {
...
}
By default if you don't declare extends Comparable, you can only access methods defined in the java.lang.Object class.

Understanding a captured type in Java (symbol '?')

What is ?. Is it related to implementation details of the Java compiler or the type is defined in the JLS.
For instance,
public interface RecipientTypeVisitor<ReturnType> {
public ReturnType visit(RecipientSetType t);
}
public class RecipientSetType extends RecipientType{
public Integer accept(RecipientTypeVisitor<?> visitor){ //Error:
return visitor.visit(this); //Cannot convert capture of #1 to Integer
}
}
But if we write this:
public interface RecipientTypeVisitor<ReturnType> {
public ReturnType visit(RecipientSetType t);
}
public class RecipientSetType extends RecipientType{
public Object accept(RecipientTypeVisitor<?> visitor){ //Ok:
return visitor.visit(this); //this implies tha the capture of #1 is
//a subtype of Object as any refrence type in Java.
}
}
That's all I can say about a captured type. So what is it actually?
The capture of a wildcard type is a type that is used by the compiler represent the type of a specific instance of the wildcard type, in one specific place.
Example: Take for example a method with two wildcard parameters, void m(Ex<?> e1, Ex<?> e2). The declared types of e1 and e2 are written exactly the same, Ex<?>. But e1 and e2 could have different and incompatible runtime types.
The type checker must not consider the types equal even if they are written the same way. Therefore, during compilation the type parameters of e1 and e2 are given specific types, new ones for each place they are used. These new types are called the capture of their declared types.
The capture of a wildcard is an unknown, but normal and concrete type. It can be used the same way as other types.
A technical description of this can be found in the JLS:
5.1.10. Capture Conversion
Let G name a generic type declaration (§8.1.2, §9.1.2) with n type parameters A1,...,An with corresponding bounds U1,...,Un.
There exists a capture conversion from a parameterized type G (§4.5) to a parameterized type G, where, for 1 ≤ i ≤ n :
If Ti is a wildcard type argument (§4.5.1) of the form ?, then Si is a fresh type variable whose upper bound is Ui[A1:=S1,...,An:=Sn] and whose lower bound is the null type (§4.1).
...
We can apply this to your example:
public Integer accept(RecipientTypeVisitor<?> visitor){ //Error:
return visitor.visit(this); //Cannot convert capture of #1 to Integer
}
A capture of the type parameter of RecipientTypeVisitor is introduced during compilation. The captured type parameter is concrete but totally unknown (the JLS calls this a "fresh type variable"), and certainly not convertible to Integer.
There is no way to denote the capture of a wildcard type directly, so you can't declare variables of that type or do much with it. You can however get a name for it indirectly by calling a generic method with it as a parameter. The JLS section I cited has a nice example of this further down:
public static void reverse(List<?> list) { rev(list); }
private static <T> void rev(List<T> list) {
List<T> tmp = new ArrayList<T>(list);
for (int i = 0; i < list.size(); i++) {
list.set(i, tmp.get(list.size() - i - 1));
}
}
You have some choices :
you have to replace ? by Integer in the signature of accept method.
you could paramtrize your method accept too :
public <T> T accept(RecipientTypeVisitor<T> visitor) {...}
this indicates your method uses some generics and links some of its components.
Every class extends from Object so the typecast from ? to Object is safe.
To get the same with Integer you can use RecipientTypeVisitor<? extends Integer> but since Integer is a final class that is useless, Integer cannot be subclassed. It is equivalent to RecipientTypeVisitor<Integer>.
Why do you want this?
You can check the official tutorial here.
The most important thing to know:
In generic code, the question mark (?), called the wildcard,
represents an unknown type.
(...)
In some cases, the compiler infers the type of a wildcard. For
example, a list may be defined as List but, when evaluating an
expression, the compiler infers a particular type from the code. This
scenario is known as wildcard capture.
For the most part, you don't need to worry about wildcard capture,
except when you see an error message that contains the phrase "capture
of".
The char ? is a wildcard meaning you don't know the type. its doesn't mean its type is Object its by definition undefined but in Java you can hold any type of object in Object type.
ArrayList<?> list1 = new Arraylist<?>(); // list of undefined objects
You cannot add objects in list1, but after casting you can.
ArrayList<Object> list2 = new Arraylist<Object>(); // list of objects of type Object
You can add objects in list2 of any type
(This is from another answer of mine, but it is more suitable for this question, explaning wildcard itself)
A wildcard ? is not a type. It is a type argument. The syntax though is very deceiving (by design).
Let's use a different syntax - if there's any 1st-level wildcard, use {} instead of <>, e.g.
List{?}, Map{String, ? extends Number}
The meaning of {?} is to declare a union type
List{? extends Number} == union of List<Number>, List<Integer>, List<Long>, ....
It's easy to see that, List<Integer> is a subtype of List{? extends Number}; and List{? extends Number} is a subtype of List{? extends Object}
In our syntax, <> is reserved for substituting type vars with types. So we write
List<String>, etc. It's easy to understand their meaning - just replace T's with String in the source code of List, we get a good-old plain class.
interface List<String>
String get(int)
This cannot be done for wildcard - it makes no sense
interface List<?>
? get(int)
So it is not allowed to new ArrayList{?}(), or class MyList implements List{?}
So, how can we use List{?}? What methods we can call on it?
When the type of an expression is a List{?}, we know that it is an object, and the object must belong to a subclass of List<x> for some unknown type x. This is wildcard capture
obj is a List{?} => obj is a List<x>, where x a subtype of Object.
Even though the exact type of x is unknown at compile time, we can still do the substitution
interface List<x>
x get(int)
so we can make sense of the call obj.get(0); it returns x, and x is a subtype of Object; so we can assign the return value to an Object.

java generics - The method evaluate(T,T) in the type T is not applicable for the arguments (Comparable, Comparable<capture#11-of ?>)

I'm having a problem with a generic method since i upgrade my java version to 8.
The call is the following:
Operator o;
Comparable a;
Comparable<?> b;
boolean match = o.evaluate(a, b)
and the method
public enum Operator{
public <T extends Comparable<T>> boolean evaluate(T value1, T value2) {
if (value1 == null && value2 != null) {
return false;
}
switch (this) {
//some code here
}
}
}
And the exception is:
The method evaluate(T, T) in the type Operator is not applicable for the arguments (Comparable, Comparable)
Thanks in advance!
evaluate expects to arguments that are known to be the same type, which can be compared to itself, but that's just not the case here.
Your generic method imposes two conditions:
That its return type extends the Comparable interface with respect to itself.
That its two parameters (and return type) are all of the same type.
However, you are passing two parameters which are not, strictly speaking, the same type. One of them is a raw Comparable object (whose base type is Object), and the other is a Comparable which is not raw, but has an unknown base type.
In reality you could assign an Integer to b, and that is not strictly compatible with a raw Comparable.
Using a.compareTo(b) would not result in a compile-time error, because a is a raw Comparable that will accept any Comparable object but will consider it to be Comparable and b can be cast to Object. That's how it works with raw types. You get only a warning, and a runtime error if the two objects do not match.
However, using b.compareTo(a) would result in a compile-time error, because b is known to have a base type, it's just not known what it is at the moment. So it's not known whether a Comparable<Object> can or cannot be cast to it.

Diamond in Generics Java 1.7 - how to write this for Java Compiler in 1.6

how can I write Java 1.7 code for a Java 1.6 compiler, where the diamond can not be used?
Example:
private ReplacableTree<E> convertToIntended(Tree<? extends E> from,ReplacableTree<E> to) {
TreeIterator<? extends E> it = new TreeIterator<>(from.getRoot());
while(it.hasNext()) {
E e = it.next().getElem();
to.add(e);
}
return to;
}
public class TreeIterator<E> implements TreeIter<Node<E>> {
....
}
It is not allowed to write...
TreeIterator<? extends E> it = new TreeIterator<?>(from.getRoot());
TreeIterator<? extends E> it = new TreeIterator<E>(from.getRoot());
TreeIterator<? extends E> it = new TreeIterator<? extends E>(from.getRoot());
Especially the third one is confusing for me. Why doesn't it work? I just want to read Elements from a Tree (which could be a subtype tree), and when puch each of it in a new Tree with Elements of type E.
Wildcard types are not permitted as type arguments in class instance creation expressions:
It is a compile-time error if any of the type arguments used in a class instance creation expression are wildcard type arguments (§4.5.1).
so the first and third variants are not valid.
Variant 2 is invalid because the TreeIterator<E> constructor wants a Node<E>, but you give it a Node<? extends E>.
As for the solution, Java 5 and 6 did not have type inference for constructors, but do have type inference for methods, and in particular capture conversion. The following ought to compile:
TreeIterator<? extends E> it = makeIterator(from.getRoot());
where
private <E> TreeIterator<E> makeIterator(Node<E> node) {
return new TreeIterator<E>(node);
}
Edit: You asked in the comment:
The contstructor parameter type for TreeIterator is Node<E>. The constructor parameter of Node<E> therefore is E. When writing variant two, eclipse says the following: The constructor TreeIterator<E>(Node<capture#2-of ? extends E> ) is undefined What does that mean?
Being a wildcard type, the type Node<? extends E> represents a family of types. Node<capture#2-of ? extends E> refers to a specific type in that family. That distinction is irrelevant in this case. What matters is that Node<? extends E> is not a subtype of Node<E>, and hence you can't pass an instance of Node<? extends E> to a constructor expecting a Node<E>.
In short you don't write Java 7 code for a Java 6 compiler - you have to use the old, duplicative non-diamond syntax. And no, you can't specify a target of 1.6 with source 1.7, it won't work!
meriton already explained it well. I just want to suggest that you could as well do it without the wildcard declaration:
TreeIterator<E> it = new TreeIterator<E>(from.getRoot());
Usually, <> means to just use the same type parameter as in the declaration to the left. But in this case, that declaration is a wildcard.
It doesn't make sense to make a constructor with a wildcard type parameter, new TreeIterator<? extends E>(...) because, usually, if you don't care what parameter to use, you should just pick any type that satisfies that bound; which could be E, or any subtype thereof.
However, in this case, that doesn't work because the constructor of TreeIterator<E> takes an object with the type parameter <E>. You didn't show the source code of TreeIterator, so I can't see what it does, but chances are that its bound is too strict. It could probably be refactored to make the type parameter <? extends E>.
But there are some cases where that is not possible. In such a case, you can still eliminate the need for the type parameter E through a "capture helper" (what meriton suggests above) to turn something which takes parameter E into something that takes a wildcard ? extends E.
I know this is an old question, but in case someone stumbles on this, I would have thought the most obvious way of writing it would have been:
private <U extends E> ReplaceableTree<E> convertToIntended(Tree<U> from, ReplaceableTree<E> to)
{
TreeIterator<U> it = new TreeIterator<U>(from.getRoot());
while(it.hasNext())
{
E e = it.next().getElem();
to.add(e);
}
return to;
}
I don't think such a change would break existing code as the type constraints are the same from the existing signature to this one.

Categories