In the contract of Comparable, there's nothing forcing an object to be comparable to itself. It's just
strongly recommended, but not strictly required that (x.compareTo(y)==0) == (x.equals(y))
which implies that it's recommended for x.compareTo(x) not to throw. But it's possible to write a
class X implements Comparable<Y> {
...
}
where X and Y are two unrelated classes. I can't see what it could be good for, but in the Java 8 version of HashMap there's even a corresponding check.
Is it allowed to implement X implements Comparable<Y> with two unrelated classes?
Does it make any sense?
I guess the answers are yes and no, but it's just a guess
Comparable promotes a contract where comparisons should be consistent with equals, i.e. (a.compareTo(b) == 0) == a.equals(b). But it does not force you to do so and any weird contract can be enforced.
So you could create a:
class DumbInteger implements Comparable<DumbInteger> {
private final int i;
public DumbInteger(int i) { this.i = i; }
public int compareTo(DumbInteger di) { return 0; }
public boolean equals(Object other) { /* checks */ return other.i == this.i; }
}
And you could also create a:
class DumberInteger implements Comparable<String> {
private final int i;
public DumberInteger(int i) { this.i = i; }
public int compareTo(String s) { return 0; }
public boolean equals(Object other) { /* checks */ return other.i == this.i; }
public static void main(String[] args) {
System.out.println(new DumberInteger(0).compareTo("abc"));
}
}
but there is probably no point in doing that. In any case this is not specific to Java 8 as the Comparable interface has been there since Java 2 and "generified" in Java 5.
But it is probably not a flaw in the Comparable interface per se, because I don't think there is a way in Java to create a generic interface I<T> that can only be implemented by classes that are subtypes of T.
I see I missed one part of the contract and also failed to see the reason why HashMap.comparableClassFor exists.
The contract says
(x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0
so whenever there's an X greater than a Y and a Y greater than an X, then the two instances of X must be comparable to each other. This doesn't leave much freedom:
Either one of the types is empty. This makes no sense at all.
Or all instances of X are smaller or equal to all instances of Y (or the other way round). This is slightly less nonsensical.
So, I'm concluding that it's possible, but makes no sense. The simplest example is
class X implements Comparable<Void> {
public int compareTo(Void v) {
return 43; // or throw or whatever, it doesn't matter
}
}
I guess that the reason for HashMap.comparableClassFor is to support different implementations of a common superclass like
abstract class AByteArray implements Comparable<AByteArray> {}
class SparseByteArray extends AByteArray {...}
class DenseByteArray extends AByteArray {...}
This seems to make sense and can be even consistent with equals.
Does it make any sense?
One issue of having two classes Comparable with each other, is because it tightly couples these classes together. This makes it difficult to re-use the class in another scenario.
Just tried it, it is possible to compare two objects with different classes.
Here is the full code.
https://gist.github.com/cevaris/11099129
X x = new X();
x.xTest = 10;
Y y = new Y();
y.yTest = 100;
System.out.println("x.compareTo(y) == -1: " + (x.compareTo(y) == -1)); //True
System.out.println("y.compareTo(x) == 1: " + (y.compareTo(x) == 1)); //True
Here is the Y implementation.
class Y implements Comparable<X> {
int yTest;
#Override
public int compareTo(X o) {
if(this.yTest < o.xTest) return -1;
if(this.yTest > o.xTest) return 1;
return 0;
}
}
Here is the X implementation.
class X implements Comparable<Y> {
int xTest;
#Override
public int compareTo(Y o) {
if(this.xTest < o.yTest) return -1;
if(this.xTest > o.yTest) return 1;
return 0;
}
}
Well, it can technically be done (as per #cevaris' answer) and can be understood when you have several ways of representing the same object, e.g. an object and its String representation. But it would only make sense if you could implement the same interface twice like:
public class CompInt implements Comparable<CompInt>, Comparable<String> {
but that is forbidden in Java because of type erasure.
Related
I am trying to understand bounded types and not quite grasping the point of them.
There is an example of bounded generics on which provides this use case:
public class NaturalNumber<T extends Integer> {
private T n;
public NaturalNumber(T n) { this.n = n; }
public boolean isEven() {
return n.intValue() % 2 == 0;
}
// ...
}
If you are going to restrict the classes that can be the parameterized type, why not just forget the parameterization all together and have:
public class NaturalNumber {
private Integer n;
public NaturalNumber(Integer n) { this.n = n; }
public boolean isEven() {
return n.intValue() % 2 == 0;
}
// ...
}
Then any class that extends/implements Integer can be used with this class.
Also, a side question: How is T extending Integer in the first example when the Java Integer class is final?
How is T extending Integer in the first example when the Java Integer class is final?
T can only be Integer, so the "extends" here is purely symbolic. (I'm starting with the side-note because, indeed, it's an example where generics are useless. I truly have no idea why the tutorial thinks this is an informative demonstration. It's not.)
Suppose instead that T extends Number:
class Example<T extends Number> {
private T num;
void setNum(T num) { this.num = num; }
T getNum() { return num; }
}
So the point of generics in general, is that you can do this:
Example<Integer> e = new Example<>();
e.setNum( Integer.valueOf(10) );
// returning num as Integer
Integer i = e.getNum();
// and this won't compile
e.setNum( Double.valueOf(10.0) );
Generics are a form of parametric polymorphism, essentially it lets us reuse code with a generality regarding the types involved.
So what's the point of a bound?
A bound here means that T must be Number or a subclass of Number, so we can call the methods of Number on an instance of T. Number is unfortunately a generally useless base class on its own (because of precision concerns), but it might let us do something interesting like:
class Example<T extends Number> extends Number {
// ^^^^^^^^^^^^^^
...
#Override
public int intValue() {
return num.intValue();
}
// and so on
}
It's more common, for example, to find T extends Comparable<T> which lets us do something more meaningful with T. We might have something like:
// T must be a subclass of Number
// AND implement Comparable
Example<T extends Number & Comparable<T>>
implements Comparable<Example<T>> {
...
#Override
public int compareTo(Example<T> that) {
return this.num.compareTo(that.num);
}
}
And now our Example class has a natural ordering. We can sort it, even though we have no idea what T actually is inside the class body.
If we combine these concepts, that:
generics allow the "outside world" to specify an actual type and
bounds allow the "inside world" to use a commonality,
we could build constructs such as:
static <T extends Comparable<T>> T min(T a, T b) {
return (a.compareTo(b) < 0) ? a : b;
}
{
// returns "x"
String s = min("x", "z");
// returns -1
Integer i = min(1, -1);
}
I wrote half of it but cannot get it 100% correct. Can anyone help me with it?
Here is my problem:
Write a parameterized class Triple that represents a trio of some type (eg, the Three of String, the Integer , etc.). For Triple class define:
-getElement method that receives an integer number (1,2 or 3) and returns the corresponding member. In the case of an illegal argument, throw an IllegalArgumentExpression exception
-setElement that receives two arguments: an integer number (1, 2 or 3) and the value to be placed as a member of the three in the corresponding position (eg, setElement(2,3) sets 3 to y value of trio)
-a constructor with 3 arguments that set the initial values of the Triple
Then you should write the ComparableTriple class, which represents the triplet. Members of triplet must be comparable (implement Comparable interface). Then expand the ComparableTriple class to implement the Comparable interface, so that triplets can be comparable by positions. For example ,(2,6,1) is in the rank before (3,4,5) but behind (2,6, -4). Same template can be applied to String etc.
Example of main code:
Triple t1 = new ComparableTriple<>(2, 6, 1);
int x = t1.compareTo(t1); // should be 0
//Triple class I wrote
public class Triple<T> {
T x, y, z;
public Triple(T x, T y, T z) {
this.x = x;
this.y = y;
this.z = z;
}
public T getElement(int position) {
if (position == 1) {
return x;
} else if (position == 2) {
return y;
} else if (position == 3) {
return z;
} else throw new IllegalArgumentException();
}
public void setElement(int position, T value) {
if (position == 1) {
x = value;
} else if (position == 2) {
y = value;
} else if (position == 3) {
z = value;
} else throw new IllegalArgumentException(); }
}
I tried writing ComparableTriple overriding CompareTo method but couldn't find the exact solution.
It's not so complicated but I'm new to Generics and can't figure some things out.
Can anyone help me about the rest of code?
public class Triple<T> implements Comparable<Triple<T>> {
... your code here
// functional interface Comparable will force you to override the method
}
But you may want to have some kind of superclass that T extends so you can access fields for the compare.
Example:
public class Triple<T extends Number> implements Comparable<Triple<T>>
There is of course the inline compare with Collections.sort(comparable) if you don't want to commit to what T is.
That should get you started. Post back/edit if you get stuck.
Cheers.
(And this is not a solution by intent). Generics are a pain and need to be slogged through to get good at.
Numbers a,b;
a = new NumbersType(1);
b = new NumbersType(1);
System.out.println(a.equals(b));
public class NumbersType implements Numbers {
int intType;
public NumbersType (int q) {
this.intType = q;
}
public boolean equals(Numbers n) {
return this == n;
}
}
public interface Numbers {
public boolean equals(Numbers n);
}
This prints false, despite both objects being the same thing. Changing return this == n to return this.intType == n.intType yields error "cannot find symbol: variable intType, location: variable n of type Numbers".
I'm fairly new to ADT and still making my way around it, so excuse me if this is a simple question. I'm not sure why it doesn't work, however, and why I cannot reference n.intType but I can this.intType.
intType is declared on NumbersType but n is a Numbers. So you can't necessarily compare them.
Now, one thing you could do is create a method on the interface that retrieves some value. I say 'some' value instead of 'the' value because the other Numbers may be some other implementation. This is an important aspect of interfaces. If you have a Numbers you cannot know that it is actually a NumbersType. (You can find that out with instanceof but this would not be a good way to program an interface implementation. The interface's declaration should specify its interactions entirely.)
But first there's kind of a side issue which is that you're declaring an overload of Object#equals. You might be intending to declare an override which is a different thing. For the purpose of my answer, I am going to show an example of both and name the overload something different.
Now here's the modified interface:
public interface Numbers {
public int getIntType();
public boolean isIntTypeEqual(Numbers n);
}
Now that you can retrieve the int, you can compare them in the implementation class.
public class NumbersType
implements Numbers {
private int intType;
public NumbersType(int intType) {
this.intType = intType;
}
#Override
public int getIntType() {
return intType;
}
#Override
public boolean isIntTypeEqual(Number n) {
return intType == n.getIntType();
}
// overriding hashCode because we are overriding equals
#Override
public int hashCode() {
return intType;
}
#Override
public boolean equals(Object o) {
if(!(o instanceof Numbers))
return false;
return isIntTypeEqual((Numbers)o);
}
}
You may want to read:
Overriding equals and hashCode in Java
Overriding and Hiding Methods
If you are just learning this stuff, equals may not be a method you should be trying to implement. It is contractual and easy to get wrong.
Another complication is declaring equals in terms of interfaces: because equals must be symmetric, the implementations must override it identically. Otherwise the contract is broken.
Your class is NumbersType not Numbers, so you should type cast a and b in your equals method. This is why you cannot do n.intType
After you type cast, your equals method should compare intTypes of the objects, not direct references. This is why it will not work.
Your naming is a little bit confusing and I would suggest studying Polymorphism in general.
I have a class with two integer members. These members are block and offset numbers. I need to compare two instances of this class with great or less signs. For example;
instance1 < instance2
statement needs to return true if
instance1.blockNumber < instance2.blockNumber;
or
instance1.blockNumber = instance2.blockNumber;
instance1.offset < instance2.offset;
As far as I know Java doesn't support operator overloading. How can I do this such comparison?
Have the class implement the Comparable interface, which gives the compareTo method. You can then use the value of the number (-1 for less, 1 for more, 0 for equals) in your if statements.
If you want to put these objects in lists (say, for sorting) you should also #Override the .equals method.
import java.util.Comparable;
public class BlockOffset implements Comparable<BlockOffset>
{
private int blockNumber;
private int offset;
#Override
public int compareTo(BlockOffset instance2) {
if (this.blockNumber < instance2.blockNumber) return -1;
if (this.blockNumber > instance2.blockNumber) return 1;
if (this.offset < instance2.offset) return -1;
if (this.offset > instance2.offset) return 1;
return 0;
}
}
If the class type is your own code than you can have it implement Comparable and define the compareTo method where you can write the logic. Then, you can compare them using compareTo.
you can implement and use comparable or comparator
I created a class called Foo. Foo has three fields, which are all ints: x, y, and z. I want to make a PriorityQueue<Foo> which prioritizes the Foo objects differently in different situations. For instance, I might want to prioritize by x value, or maybe by y value, or maybe by z. But I won't know which value I want to prioritize by until runtime. I've heard you can use comparators to somehow impose an ordering on the fly, which I think would be perfect here.
I'm confused as to exactly how I would do this though. Could someone please show me an example if say I wanted to prioritize on x using a comparator (without having to override the compareTo function in my Foo class)?
Thank you very much.
A comparator is a parameterized interface that allows you to define how two instances of the parameterized type can be compared. let's assume you have the following class
class Foo {
int x;
int y;
int z;
}
Then to define a comparator that orders elements based on their x value then y then z we'd do the following
class XyzComparator implements Comparator<Foo> {
#Override
public int compare(Foo foo1, Foo foo2) {
if(foo1.x != foo2.x) {
return Integer.compare(foo1.x, foo2.x);
}
if(foo1.y != foo2.y) {
return Integer.compare(foo1.y, foo2.y);
}
return Integer.compare(foo1.z, foo2.z);
}
}
Similarly you can define comprators that compare elements based first on their y value then x the z...etc
Finally at runtime you can instantiate a PriorityQueue with that comparator
PriorityQueue<Foo> queue;
if(x_then_y_then_z) {
queue = new PriorityQueue<Foo>(10, new XyzComparator());
} else if (y_then_x_then_z) {
queue = new PriorityQueue<Foo>(10, new ZxyComparator());
}
For more information take a look at the priority queue javadoc as well as the comparator javadoc
Edit: Please see #buritos comment regarding integer overflow.
Comparator<Foo> comparator = new Comparator<Foo>() {
#Override
public int compare(Foo o1, Foo o2) {
if (o1.x > o2.x)
return 1;
else if(o1.x < o2.x)
return -1;
else
return 0;
}
};
PriorityQueue<Foo> queue = new PriorityQueue<Foo>(10, comparator);
...