Casting a real number to Comparable in java? - java

Let´s say I have a method that inserts a number into a Binary Tree
public BinaryNode insert(Comparable x, BinaryNode t) {
//Logic here
}
BinaryTree t = new BinaryTree();
How would I cast a number , for example 4 to match the right paremeters on this methods? Ex.
t.insert(4, t ) // I know this wont work, but I wanted to to something like this

It should work fine - it'll be boxed into an integer. Sample code:
public class Test {
static void foo(Comparable<?> c) {
System.out.println(c.getClass());
}
public static void main(String args[]) throws Exception {
foo(10);
}
}
This works even if you use the raw type Comparable, although I'd recommend against that.
Admittedly I strongly suspect that you'd be better off making your BinaryNode (and presumably BinaryTree) class generic, at which point you'd have:
// You shouldn't need to pass in the node - the tree should find it...
BinaryTree<Integer> tree = new BinaryTree<Integer>();
tree.insert(10);

It should work if you pass new Integer(4) instead of 4 assuming you are using Java version prior to 5 (as you are saying it's not working).

Related

Exact type match in Java generics

The following Java code is valid - no compile- or run-time errors.
public class Test {
public static void main(String[] args) {
System.out.println(
new Comparator<>("hello", 5).areEqual()
);
}
private static class Comparator<T> {
private final T left;
private final T right;
public Comparator(T left, T right) {
this.left = left;
this.right = right;
}
public boolean areEqual() {
return left.equals(right);
}
}
}
I expect the values "hello" and 5 are casted to Object, which is a common parent.
Is there a way to write the class in such a way that passing other than objects of the same type causes a compile-time error? I.e. passing objects casted to a common ancestor not allowed.
Not really, no.
Because your generic type is unbound, there's only one common ancestor: Object - as you correctly surmise.
The only thing you can realistically do in this scenario is ensure that equals is correctly defined for objects you want to pass through this method. You would also want to handle cases in which you pass null as an argument, too.
Just specify the type in the generic parameters...
public class Test {
public static void main(String[] args) {
System.out.println(
new Comparator<String>("hello", 1).areEqual()
);
}
...
}
If you do this with your current code, you will get a compiler error.
When you leave it unspecified, Java does not bother doing any type checking and simply uses the super type (Object) as the type for both.
You will also find the same issue when you do something like:
System.out.println(Arrays.asList("hello", 1)); // Will compile
vs
System.out.println(Arrays.<String>asList("hello", 1)); // Will not compile
So, I think it is not possible by using generic. But you can restrict it by checking the Object type in the constructor like below code.
public Comparator(T left, T right) {
if(left.getClass()!=right.getClass()) {
throw new IllegalArgumentException("All values passed needs to be object of same class");
}
this.left = left;
this.right = right;
}
However, this will not give you compile time error but it will through Exception when a different type of object passed to the constructor.
As #Makoto already answered, the short answer is no.
If you look at code snippet for equals method in your favorite IDE then you'll see that it has line like if (getClass() != another.getClass()) return false; It means that you check class of object inside equals. You could do the same at areEqual method but probably it's not needed - it should be implemented on another level

Calling a function on an enum constant obtained by values()

I'm trying to generalise some code by iterating over all constants of an enum to receive the same specific argument from each one.
Specifically I have an enum P with some constants A,B,C.
Each of these constants is itself an enum and implements an interface I that defines a function f.
P.values() gives me an array P[] A = {A,B,C}, however I can't call A[i].f() since A[i] is of course of type P which doesn't implement I.
Now in my understanding a function can return an interface, but I can not instantiate it and therefore can't cast to it.
Should I overwrite values() for P to return I[]? If so, how would I do that since I can't cast to I? Or is there another solution?
I am working in eclipse but assuming that it's complaints are indicative of a true mistake, not just eclipse not recognising types.
Since I'm somewhat new to Java I would also appreciate any links to resources that explain the underlying rules of type matching/checking.
This seems to do what you describe - perhaps I have misunderstood your question though. If so please explain further.
interface I {
void f ();
}
enum P implements I{
A,
B,
C {
// Demonstrate enum-specific implementation.
#Override
public void f () {
System.out.println("SEEEEEE!");
}
};
// By default `f` prints the name of the enum.
#Override
public void f () {
System.out.println(name());
}
}
public void test() throws Exception {
for ( I i : P.values()) {
i.f();
}
}

Java - interface passing itself as a parameter [duplicate]

I had an interview days ago and was thrown a question like this.
Q: Reverse a linked list. Following code is given:
public class ReverseList {
interface NodeList {
int getItem();
NodeList nextNode();
}
void reverse(NodeList node) {
}
public static void main(String[] args) {
}
}
I was confused because I did not know an interface object could be used as a method parameter. The interviewer explained a little bit but I am still not sure about this. Could somebody enlighten me?
This is in fact one of the most common and useful ways to use an interface. The interface defines a contract, and your code can work with any class that implements the interface, without having to know the concrete class - it can even work with classes that didn't exist yet when the code was written.
There are many examples in the Java standard API, especially in the collections framework. For example, Collections.sort() can sort anything that implements the List interface (not just ArrayList or LinkedList, though implementing your own List is uncommon) and whose contents implement the Comparable interface (not just String or the numerical wrapper classes - and having your own class implement Comparable for that purpose is quite common).
It's not the interface "object" being passed to the method, still just a regular object. It's just a way of saying "this parameter will accept any object that supports this interface". It's equivalent to accepting some object of a base class type, even if you're passing in a subclass.
This is called programming to interfaces. You don't code to a specific implementation class of node lists but to the interface implemented by all those implementations.
That way your code will still work if someone writes a new and much better implementation of NodeList after you wrote your reverse method and you don't have to adapt your code for each new implementation of NodeList.
The argument needs an object, which class implements an interface (the parameter).
In pseudo Java the code:
void reverse(NodeList node) {
// your code
}
is equal to:
reverse(x) {
if(x == null || x instanceof NodeList) {
// your code
}else throw new RuntimeException("Some sort of error.");
}
Note; read more on Interfaces here: http://java.sun.com/docs/books/tutorial/java/IandI/interfaceAsType.html
This is one possible implementation:
public class ReverseList {
interface NodeList {
int getItem();
NodeList nextNode();
}
static class Node implements NodeList {
private int item;
private Node next;
#Override
public int getItem() {
return item;
}
public void setItem(int si) {
item = si;
}
#Override
public NodeList nextNode() {
return this.next;
}
public void setNext(Node n) {this.next=n;}
}
Node reverse(NodeList head) {
Node node = (Node) head;
Node previous = null;
while(node.nextNode() !=null) {
Node tempNext = (Node) node.nextNode();
node.setNext(previous);
previous = node;
node = tempNext;
}
node.setNext(previous);
return node;
}
public static void main(String[] args) {
//Initialization block
ReverseList rl = new ReverseList();
Node n1= new Node(); n1.setItem(1);
Node n2=new Node(); n2.setItem(2);
Node n3 =new Node(); n3.setItem(3);
n1.setNext(n2); n2.setNext(n3); n3.setNext(null);
//Reversing the list
System.out.println("Before reversal");
System.out.println(n1.getItem() +"->"
+ n1.nextNode().getItem() + "->"
+ n1.nextNode().nextNode().getItem() + "->"
+n1.nextNode().nextNode().nextNode());
rl.reverse(n1);
System.out.println("\nAfter reversal");
System.out.println(n3.getItem() +"->"
+ n3.nextNode().getItem() + "->"
+ n3.nextNode().nextNode().getItem() + "->"
+n3.nextNode().nextNode().nextNode());
}
}
Program output:
Before reversal
1->2->3->null
After reversal
3->2->1->null
I am very curious to know if this problem can be solved by using an anonymous class. Any ideas?
Had this same confusion while learning lambda stuff.
This video didnt explain the concept, but it's a clear way for you to see how it works in terms of passing an interface as a parameter.
https://www.youtube.com/watch?v=mk3erzL70yM
The major benefit of using interfaces, IMHO, is being able to test easily. Suppose you have an interface called, PatientManager.
You can write specific unit tests for imaginable things like "CachingPatientManager" or "LDAPPatientManager", the use case could be myriad.
The benefit is because the programming to interface becomes highly reusable and testable.
You cannot create an instance (/object ) of an Interface.
Yes, you can pass Interface as a parameter in the function. But the question seems incomplete. Interface isn't implemented by any class. Something is missing. If you try to run this, compiler will not show any error.
But, in the reverse() method you need to create an instance of class that implements NodeList interface. I hope this makes sense.

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 subclass does not recognise its generic superclass

I need help with a problem I have with generics in Java. I'm writing this Computer algebra system, where the user enters a math expression and the system works with it in different ways (expand it, simplify it etc). It worked fine for expressions containing natural numbers, and I wanted to expand it to work with mathematical sets as well. Instead of +, you would have the intersection operator, etc.
At first, I started recording everything for the sets, but then I realized this was probably not good and started using generics.
Instead of having one parse tree like MathExpr and one like SetExpr, I thought I could just have a generic Expression<T> and build a base class Number and a base class Set.
To try to clarify, I want a mathematical expression like (2 * a) + (3 + 2) to be an instance of a class Expression<Number> and a set expression like (A ∪ B) ∩ C to be an instance of Expression<Set>. I can then perform different operations on this, like calculate the depth etc.
The + operation is implemented as one class, the * as one class etc. Both these classes are subclasses of an abstract class called TwoExpr which in turn is a subclass of the abstract class Expr. This is how I have done it now and everything works fine.
When I wanted to change my code I made my Expr class generic. That is Expr<T>. I also changed TwoExpr to TwoExpr<T> and created a base class Number.
The problem is, now I can't seem to instantiate objects of the type Sum<Number>.
I get a "Type mismatch: cannot convert from Sum to Expr<Number>" error. But Sum is a subclass of TwoExpr<Number>, which in turn is a subclass of Expr<Number>. As you may realize, I can't make the class Sum generic and call it Sum<Number>, because all arithmetic operations don't have analogues for sets.
I have always been able to create objects like
Expr zero= new Leaf(0);
Variable a = new Variable("a");
Expr aPlusZero = new Sum(a, zero);
When I changed to generics, the same code looks like this:
Expr<Number> zero= new Leaf<Number>(new Number(0)); //works fine
Variable<Number> a = new Variable<Number>("a"); //works fine
Expr<Number> APlusZero=new Sum(a,zero); //gives a "Type mismatch:
//cannot convert from Sum to Expr<Number>" error
How come it doesn't recognize that Sum(a,zero) is a subclass of Expr<Number>, when it says in the declaration of Sum
public class Sum extends TwoExpr<Number> {
public Sum(Expr<Number> a, Expr<Number> b) {
super(a, b);
}
...
}
and in the declaration of TwoExpr
public abstract class TwoExpr<T> extends Expr<T> {
protected Expr<T> a;
protected Expr<T> b;
public TwoExpr(Expr<T> a, Expr<T> b) {
this.a=a;
this.b=b;
}
...
}
I know that Lizkows substitution principle doesn’t apply for generic arguments. But Number isn’t a subclass of anything (except Object) and don't have any subclasses.
I hope I've been fairly clear about what I'm trying to do and what problem I have. Does anybody have any idea how to solve it? Please tell me if anything was unclear in the above or if you want more code.
Thanks in advance.
Mattias
I think your problem is in the classes you did not show, I tried the following and it works:
Expr<Number> zero= new Expr<Number>();
Expr<Number> a= new Expr<Number>();
Expr<Number> APlusZero=new Sum(a,zero);
Might it be that Variable is not an Expr?
UPDATE:
I played a little creating Variable and Leaf as I imagine them and it all works:
public class Number {
public Number(int i){}
}
public class Variable<T> extends Expr<T> {
public Variable(String s){}
}
public class Leaf<T> extends Expr<T> {
public Leaf(T t) {
super();
}
}
public class Expr<T> {
}
public class TwoExpr<T> extends Expr<T> {
public TwoExpr(Expr<T> a, Expr<T> b) {
}
}
public class Sum extends TwoExpr<Number> {
public Sum(Expr<Number> a, Expr<Number> b) {
super(a, b);
}
}
public class AllTogether {
public static void main(String[] args) {
Expr<Number> zero= new Leaf<Number>(new Number(0));
Variable<Number> a = new Variable<Number>("a");
Expr<Number> APlusZero=new Sum(a,zero);
}
}
If you take the extends Expr from Variable it does give the error you experience, might it be this the cause?
Maybe try to debug it:
Object = new Sum(a,zero);
System.out.println(o.getClass().getGenericSuperclass());
Furthermore maybe is a better solution for your system.
Sum does not import Types.Number. So it's not Expr<Types.Number> but Expr<java.lang.Number>. I would assume this would give a compilation error not only on the assignment but also on the construction of new Sum(vA, zero), but maybe the compiler sees the other error first.

Categories