Generics and compareTo() method - java

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.

Related

Generic Method Type Parameter Declaration

So, good ol' Dietel states, "All generic method declarations have a type-parameter section delimited by angle brackets (< and >) that precedes the methods return type," (Deitel, 2012, italicized emphasis mine). The example given is as follows:
public static < T > void printArray (T[] inputArray)
{
for (T element : inputArray)
(
System.out.printlf("%s", element);
}
That makes sense to me. I get that. But, here is my question, not addressed explicitly in the book.
I have a very simple class to demonstrate:
public class Pair<F, S>
{
private F first;
private S second;
}
Now, according to Deitel, "ALL" generic method declarations must contain a type-parameter section. So, naturally, I want to add a get() and set() method to my class example. So, I do this:
public class Pair<F, S>
{
private F first;
private S second;
// Here, I'll do one instead of both for the sake of shortening the code
public < F > F getF()
{
return F;
}
// And the Accessor:
public < F > void setF(F first)
{
this.first = first;
}
}
So, here's the deal. The Eclipse IDE gives me a warning ahead of my attempt to compile (the Java version of Intellisense) that states, "The type parameter F is hiding the type F". Now, I don't particularly trust Dietel for Java - and am growing to understand that they are not particularly reliable (in that they often leave out important distinctions). So, I went to the Oracle Documentation for what I am doing and - GUESS WHAT - they mention nothing of the sort, unless you're talking about 'upperbounded' type parameters.
Here's the question (it's threefold):
Is the difference here the `static' qualifier, i.e. that the method I am writing appears in a class?
What on Earth is Dietel doing, particularly as implementation of their suggestions, here, yields a warning?
By changing the class type parameters, I get rid of the warning. So, conceptually, what is going on to where the method parameter type is "hiding" the class parameter type?
The JLS specifically designates a generic method as one that declares type parameters. (JLS) So the confusion here is that Deital has said that "all generic methods have a type parameter section" but presumably not specifically pointed out that this is their definition. It is more clear to say that "a generic method is one that has a type parameter section".
As noted in a comment, when you have type parameters declared by a class, you do not need to redeclare them at the method. As noted by Eclipse, doing so actually declares new type parameters which hide the ones declared by the class.
When they are declared on the class you can use them directly:
class Pair<F, S> {
F getF() { ... }
S getS() { ... }
void setF(F f) { ... }
void setS(S s) { ... }
}
The purpose of a generic method is to use it parametrically. The given example is not particularly good for understanding because the generic type is actually unused: the printf overload for Object is called. It can be rewritten without generics with no change to its functionality:
public static void printArray(Object[] arr) {
for(Object o : arr) {
System.out.printf("%s", o);
}
}
The easiest example for understanding the use of a generic method is the implementation of Objects#requireNonNull which is something like this:
public static <T> T requireNonNull(T obj) {
if(obj == null)
throw new NullPointerException();
return obj;
}
It takes any object and conveniently returns it as a T:
// T is inferred
String hello = Objects.requireNonNull("hello world");
// T is provided as a witness (rarely necessary)
Integer five = Objects.<Integer>requireNonNull(5);
It is the simplest generic method.

Casting error in BST iterator

I'm having trouble with casting for generics. What I'm trying to do is use a generic iterator to help me print out what is in my Binary Search Tree. However the for loop I'm implementing says their are incompatible types and therefore will not run. Wanted to get some insight on to what I'm doing wrong here.
public class BinarySearchTree<AnyType extends Comparable<? super AnyType>> implements Iterable{...}
public class MainBST {
public <AnyType> void print(BinarySearchTree<? extends AnyType> t ) {
for(AnyType x : t) //incompatible type
System.out.print(x + ", ");
System.out.println("\n");
}
}
The problem is your BinarySearchTree declaration. It's implementing the raw Iterable type, when it should be implementing Iterable<AnyType>. With the raw Iterator type, the code using the enhanced-for loop only knows that the values will be compatible with Object - so you could change your loop to for (Object x : t) but of course that's not what you really want.
I've reproduced the compile-time error you've shown, and then fixed it by changing the declaration to:
class BinarySearchTree<AnyType extends Comparable<? super AnyType>>
implements Iterable<AnyType> {
...
}
(And then changing the iterator method to return Iterator<AnyType> of course.)

Java - generic types

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>.

Java has the weirdest error

When I run the following code:
class zTree<T>
{
ArrayList<ArrayList<T>> table = new ArrayList<ArrayList<T>>();
int height = 0;
<T> void zTree(BinaryTree<T> tree)
{
recIt((BinaryTree<T>)tree, 1);
}
void recIt(BinaryTree<T> tree, int fromRoot)
{
if(!(tree.isEmpty()))
{
ArrayList<T> tempList = (ArrayList<T>)table.get(fromRoot);
tempList.add((T)tree.getData()); // add data to table
recIt(tree.left,fromRoot+1); // recursive left,
recIt(tree.right,fromRoot+1); // right
}
else
{
height = fromRoot-1;
}
}
}
Javac returns this error.
zTree.java:15: recIt(structures.tree.BinaryTree<T>,int) in zTree<T> cannot be applied to (structures.tree.BinaryTree<T>,int)
recIt((BinaryTree<T>)tree, 1);
^
1 error
I don't care about he efficiency of my code. I care to understand what is going wrong but javac is clearly not much help for once as it's telling me that (x,y) can not be applied to (x,y) ... but why?
The problem is that the T in the zTree method (which bizarrely has the same name as its enclosing class - don't do that) isn't the same as the T in the zTree class, because the method is generic:
<T> void zTree(BinaryTree<T> tree)
If you make it not generic, it should be fine, as now the T type parameter within the method will mean the same as the T in the method you're calling.
void zTree(BinaryTree<T> tree)
I would strongly advise use to start following Java naming conventions, and definitely don't create any methods with the same name as the class in which they're declared.
If that method was meant to be a constructor, you should get rid of the return type:
zTree(BinaryTree<T> tree)
(And still fix the class name.)
You are declare the method zTree generic when you say
<T> void zTree(BinaryTree<T> tree)
I suspect that you wanted to create a constructor. If so, don't use a return type. However, you've already declared your class generic; just use your class's T:
zTree(BinaryTree<T> tree)
Additionally, conventionally, Java class names start with an uppercase letter, e.g. ZTree.

Java Generics: How does Java determine whether it's a set or get process for a bounded wildcard

Ok guys. This is a revision
class Node<E> { // (1)
private E data; // Data (2)
private Node<E> next; // Reference to next node (3)
Node(E data, Node<E> next) { // (4)
this.data = data;
this.next = next;
}
public void setData(E e) {} // (5)
public void xxxData(E e) {} // (6)
public E getData(E e) {return null;} // (7)
public static void main(String [] args) {
Node<? extends Integer> n1 = new Node<Integer>(1,null); //8
Node<? super Integer> n2 = new Node<Integer>(1,null); //9
n1.setData(new Integer(1)); //10 compiler error
n1.xxxData(new Integer(1)); //11 compiler error
n2.setData(new Integer(1)); //12 ok
}
}
Here's a rewrite hopefully i can convey my confusion nicely.
1. n1 is upper bounded wildcard. So this wont allow adding of records. Clause 10 proves this.
2. clause 11 also proves that method names (in this case 'SET' to determine adding of records) not being used since xxxData method gives the same compiler error.
3. n2 is lower bounded wildcard. Since method names doesn't play a role here, how does compiler knows that setData method can be used on n2 (since n2 is a lower bounded wildcard and this wildcard allows for adding of records)? Basically what the difference on method setData on clause 10 and 12?
Bear in mind nothing happens in those methods. It's empty or returning null.
If I understood your question properly, I guess it's because Integer is both super- and subtype to itself.
Yes, you are confusing three things:
java method names are just names, you can call them anything you want
there is a convention in Java to use what are called getters and setters. If you have a field (data or next in your example), then you define two methods:
.
public void setData(E data) {
this.data = data;
}
public E getData() {
return this.data;
}
This convention is called Java Beans. You'd do the same for node by the way.
3) Java selects the method to call based upon the types of the parameters that you pass to the method. This is called method overloading. It means that you can define things like:
public void setFile(String name) {
// do something here
}
public void setFile(File file) {
// do something here
}
so you can call:
setFile(new File("barbar"));
or
setFile("c:\stuff");
and the correct method will be chosen.
The generic types that you have just confuse the situation even more :-)
Java doesn't care about get/set in the method name; it's all based on method parameter types. Consider
interface G<T>
T f1();
void f2(T t);
void f3();
Substitute T with different types, methods signatures are changed too; for example, G<Int> methods
Int f1();
void f2(Int t);
void f3();
So we can do the following
G<Int> o = ...;
Int i = o.f1();
o.f2(i);
o.f3();
What happens with wildcards? Actually compiler can't directly reason about wildcards; they must be replaced with fixed albeit unknown types; this process is called "wildcard capture".
For example, given a G<? super Int> type, compiler internally treats it as G<W>, where W is an unknown supertype of Int. G<W> has methods
W f1();
void f2(W t);
void f3();
So we can do
G<? super Int> o = ...;
Object i = o.f1(); // f1() returns W, which is subtype of Object
o.f2( new Int(42) ); // f2() accepts W; Int is a subtype of W, accepted.
o.f3();
Similarly
G<? extends Int> o = ...; // G<W>, where W is a subtype of Int
Int i = o.f1(); // f1() returns W, which is subtype of Int
o.f2( new Int(42) ); // error: f2() accepts W; Int is NOT a subtype of W
o.f3();
I think i have the answer to this problem. It's true that method name doesn't play a role here but rather whether the reference assignment is valid.
Compiler error for clause 10 & 11 is due to the following in terms of parameter assignment.
? extends Integer = Integer
One cannot assign the Integer to ? extends Integer since the ? extends Integer could have type which is lower than Integer (figuratively speaking).
Of course this works for clause 12.
The question really changed now... The answer to this is PECS
(or here, page 28)
Also see, What is PECS (Producer Extends Consumer Super)? or How can I add to List<? extends Number> data structures?

Categories