I have been reading about interface in java. Overall I understood the concept except one problem. In http://goo.gl/l5r69 (docs.oracle), in the note it is written that we can type cast an interface and a class implementing it. That is
public interface Relatable {
public int isLargerThan (Relatable other) ;
}
public class RectanglePlus implements Relatable {
public int width = 0;
public int height = 0;
public Point origin;
// four constructors
// ...
// a method for computing the area of the rectangle
public int getArea() {
return width * height;
}
// a method required to implement the Relatable interface
public int isLargerThan(Relatable other) {
RectanglePlus otherRect = (RectanglePlus) other;
if (this.getArea() < otherRect.getArea()) {
return -1;
} else if (this.getArea () > otherRect.getArea()) {
return 1;
} else {
return 0;
}
}
}
How can otherRect (which is a interface) be casted to a RectanglePlus. The confusion is, RectanglePlus is a class having variables, which are not present in the otherRect which is an interface
I have to admit that the example in the java doc you showed is simply bad and confusing.
It's bad because it contains an unsafe cast down the hierarchy.
It is always safe to cast up (from implementing class to interface/superclass), but casting down should be avoided when possible.
Ideally, the Relatable interface should also contain getArea() method:
public interface Relatable {
public int isLargerThan(Relatable other);
public int getArea();
}
Now you don't need the ugly cast, simply:
public int isLargerThan(Relatable other) {
if (this.getArea() < other.getArea()) {
return -1;
} else if (this.getArea () > other.getArea()) {
return 1;
} else {
return 0;
}
}
is enough. I also think that isLargerThan(Relatable other) is a bad name (larger in terms of what?). It should probably be something like hasBiggerArea(Relatable other) so that it explains what we are actually comparing (only "larger" is rather vogue).
Your interface is pretty similar to Comparable, (Are you sure Comparable isn't what your looking for?) so maybe you should add a generic to it:
public interface Relatable<T extends Relatable> {
public int isLargerThan(T t);
}
And then your class will start as:
public class RectanglePlus implements Relatable<RectanglePlus> {...
So your RectanglePlus instance will be compared with other RectanglesPlus elements only.
If this does not suit what you need, then you have to choose what will happen when you are comparing two different classes:
public class RectanglePlus implements Relatable {
public int width = 0;
public int height = 0;
public Point origin;
public int getArea() {
return width * height;
}
public int isLargerThan(Relatable other) {
if (!(other instanceof RectanglePlus)) {
return 1; // I'm bigger than any other class!
}
RectanglePlus otherRect =(RectanglePlus)other;
return this.getArea() - otherRect.getArea();
}
}
Or, a third option, you can add another method to your interface to obtain the measureable, realatable value of an object. Then you could add a default implementation to isLargerThan, if you are using Java 8:
public interface Relatable<T extends Relatable> {
public default int isLargerThan(T t) {
return this.getRelatableValue - t.getRelatableValue();
}
public int getRelatableValue();
}
In the method declaration public int isLargerThan(Relatable other){...} the parameter other is declared to be a reference to an object whose class implements the interface Relatable.
In the method body, the expression (RectanglePlus)other means to check that the object is of class RectanglePlus or a subclass of that (and throw a ClassCastException if it isn't). Now, a RectanglePlus is Relatable, but the inverse isn't necessarily true; the cast here ensures that other will either be RectanglePlus; if it's not further instructions will not be executed because an exception will have been thrown.
We can type cast any object(of class C) stored in a variable of type T1 (interface or class) into a variable of type T2 if T2 is a super class or super interface of class C or T2==C otherwise a ClassCastException will be thrown.
So in your case if an object obj of class Foo implements Relatable is passed to isLargerThan method then it will throw ClassCastException, because obj's class Foo is not a super class of RectanglePlus.
One aspect that hasn't been touched on in other answers is the fact that the example in the Oracle docs has a clear problem: if Relatable is only meant to be analogous to Comparable, then there needs to be a specialization for shapes in order to avoid the cast in the isLargerThan method. Perhaps, for example, an interface called RelatableShape, which itself extends Relatable, and provides for the getArea() method. Then, you could have Circle, Hexagon, Rectangle, etc. classes that implement RelatableShape (the interface with isLargerThan and getArea), and the isLargerThan() method would not need to cast its argument to a particular concrete class (since the parameter could be guaranteed to implement RelatableShape, and getArea() would always be available).
Thus, though the Oracle documentation is showing something that is valid Java, it's also showing something that's necessary because of a bad design. Keep that in mind. Casting is almost never necessary in real code.
It's rather simple your mehtod
public int isLargerThan(Relatable other)
just asks for an argument that implements Relatable. It could be an object of any class that implements Relatable. As long as there is something like
public class SomeName implements Relatable { /* Implementation */ }
in the class, you can treat objects of that class as Relatable.
But that does not mean that these objects are not of their own type. If you have the following classes
public class Square implements Relatable {
public int isLargerThan(Relatable other) {
// Implementation
}
// Square specific implementation
}
and
public class Rectangle implements Relatable {
public int isLargerThan(Relatable other) {
// implmentation
}
// Rectangle specific implemenation
}
you can call the interface methods like this:
/* ... */
public static int check(Relatable a, Relatable b) {
return a.isLargerThan(b);
}
/* ... */
Square s = new Square();
Rectangle r = new Rectangle();
System.out.println("Check: " + check(s, r));
ATTENTION:
Because several different classes can implement Relatable you have to check the type of the argument to isLargerThan, otherwise you run into type cast exceptions.
Maybe you can specify something like this in Relatable
public int getSize();
Than you could write your isLargeThan method like this:
public int isLargerThan(Relatable other) {
if (this.getSize() < other.getSize())
return -1;
else if (this.getSize() > other.getSize())
return 1;
else
return 0;
}
Then there would be no need for a type cast.
Related
I am going through the "Java Generics and Collections" book by Naftalin. I want to check my understanding regarding a point that they make in Chapter 3.
They explain about how the Comparable interface allows us to control what types we can compare with one another. In the below code example that they provide, they say that it is not possible to compare Apple with Orange. To be precise, this is what they say:
Since Apple implements Comparable<Apple>, it is clear that you can compare apples with apples, but not with oranges.
But as you can see, on the last line of the main method, we are able to do so, because the compareTo from the base class ends up being called. So I presume the solution to this would be to duplicate the implementation of the compareTo method by moving it to each of the Apple and Orange classes? And removing it from the base Fruit class? Or is there a better way to do this?
abstract class Fruit {
protected String name;
protected int size;
protected Fruit(String name, int size) {
this.name = checkNotNull(name);
this.size = size;
}
#Override
public boolean equals(Object o) {
if (o instanceof Fruit) {
Fruit that = (Fruit) o;
return this.name.equals(that.name) && this.size == that.size;
}
return false;
}
#Override
public int hashCode() {
return name.hashCode() * 29 + size;
}
protected int compareTo(#Nullable Fruit that) {
if (this.size > that.size) {
return 1;
} else if (this.size < that.size) {
return -1;
} else return 0;
}
}
}
class Apple extends Fruit implements Comparable<Apple> {
public Apple(int size) {
super("Apple", size);
}
#Override
public int compareTo(Apple apple) {
return super.compareTo(apple);
}
}
class Orange extends Fruit implements Comparable<Orange> {
protected Orange(int size) {
super("Orange", size);
}
#Override
public int compareTo(Orange orange) {
return super.compareTo(orange);
}
}
class TestFruit {
public static void main(String[] args) {
Apple apple1 = new Apple(1);
Apple apple2 = new Apple(2);
Orange orange1 = new Orange(1);
Orange orange2 = new Orange(2);
List<Apple> apples = List.of(apple1, apple2);
List<Orange> oranges = List.of(orange1, orange2);
// Both of these work as expected
System.out.println(Collections.max(apples)); // Apple{name=Apple, size=2}
System.out.println(Collections.max(oranges)); // Orange{name=Orange, size=2}
// But now, as expected, when you try to create a mixed list, it will no longer work
List<Fruit> mixed = List.of(apple1, orange2);
// Compile error on the below line
System.out.println(Collections.max(mixed));
// But this also works now, because compareTo from the Fruit class
// is being used here.
System.out.println(apple1.compareTo(orange1)); // -1
}
}
A class has method compareTo(Fruit) doesn't mean it implements Comparable<Fruit>. Only the opposite is correct.
In java API, method requiring comparison is based on Comparable interface, instead of checking if the class has compareTo method.
Since Fruit does not implements Comparable<Fruit>(it is a design problem if it implements), what the book say is correct. You can see Collections.max can't be called with List<Fruit>.
IMO, what make us confuse is the method name compareTo in Fruit.
The name is ambiguous to Comparable method,
So by convention, we should name a method compareTo only when we implements Comparable.
Wrong method may be called due to static binding
// Apple#compareTo is called
System.out.println(apple1.compareTo(apple2));
Fruit fruitWhichIsApple2 = apple2;
// Fruit#compareTo is called
System.out.println(apple1.compareTo(fruitWhichIsApple2));
Although it is marked as protected(which avoid usage from other package class), it is much clear if we rename it to something else.
Here's an example from my code:
Baseclass:
abstract class AbstractBase implements Comparable<AbstractBase> {
private int a;
private int b;
public int compareTo(AbstractBase other) {
// compare using a and b
}
}
Implementation:
class Impl extends AbstractBase {
private int c;
public int compareTo(Impl other) {
// compare using a, b and c with c having higher impact than b in AbstractBase
}
FindBugs reports this as an issue. But why is that? What could happen?
And how would I correctly implement a solution?
Impl#compareTo(Impl) is not overriding AbstractBase#compareTo(AbstractBase) since they don't have the same signature. In other words, it won't be called when using Collections#sort for example.
EDIT: Added solution without casting
If you don't want to cast you could try the following.
Alter your baseclass to:
abstract class AbstractBase<T extends AbstractBase<?>> implements Comparable<T> {
//...
public int compareTo(T other) {
//...
}
}
And you Impl class to:
class Impl extends AbstractBase<Impl> {
//...
#Override
public int compareTo(Impl other) {
//...
}
}
Solution with casting:
A possible solution would be to override the compareTo(AbstractBase) method in the Impl class and explicitly check if an instance of Impl is passed in:
class Impl extends AbstractBase {
//...
#Override
public int compareTo(AbstractBase other) {
if (other instanceof Impl) {
int compC = Integer.compare(c, ((Impl) other).c);
if (compC == 0) {
return super.compareTo(other);
}
return compC;
}
return super.compareTo(other);
}
}
The following is something that I tried. Not exactly sure this is the reason why findbugs gives the error.
See the following code with a hypothetical implementation of the compareTo method.
Comparing the same objects results in different outputs.
public class Main
{
public static void main(String[] args)
{
Impl implAssignedToImpl = new Impl(1, 2, 3);
Impl otherImpl = new Impl(3, 2, 1);
System.out.println(implAssignedToImpl.compareTo(otherImpl)); // prints -2
AbstractBase implAssignedToAbstract = implAssignedToImpl;
System.out.println(implAssignedToAbstract.compareTo(otherImpl)); //prints 0
}
}
class AbstractBase implements Comparable<AbstractBase>
{
private int a;
private int b;
public AbstractBase(int a, int b)
{
super();
this.a = a;
this.b = b;
}
public int compareTo(AbstractBase other)
{
return (a + b) - (other.a + other.b);
}
}
class Impl extends AbstractBase
{
private int c;
public Impl(int a, int b, int c)
{
super(a, b);
this.c = c;
}
public int compareTo(Impl other)
{
return super.compareTo(other) + (c - other.c);
}
}
Building on my hypothetical compareTo, following seems to be a good solution. You can try to have a method similar to getSum which gives the object instance a value.
public class Main
{
public static void main(String[] args)
{
Impl implAssignedToImpl = new Impl(1, 2, 3);
Impl otherImpl = new Impl(3, 2, 1);
System.out.println(implAssignedToImpl.compareTo(otherImpl)); // prints 0
AbstractBase implAssignedToAbstract = implAssignedToImpl;
System.out.println(implAssignedToAbstract.compareTo(otherImpl)); //prints 0
}
}
class AbstractBase implements Comparable<AbstractBase>
{
private int a;
private int b;
public AbstractBase(int a, int b)
{
super();
this.a = a;
this.b = b;
}
public int compareTo(AbstractBase other)
{
return getSum() - other.getSum();
}
public int getSum()
{
return a + b;
}
}
class Impl extends AbstractBase
{
private int c;
public Impl(int a, int b, int c)
{
super(a, b);
this.c = c;
}
#Override
public int getSum()
{
return super.getSum() + c;
}
}
As sp00m said, your Impl#compareTo(Impl) has a different signature than AbstractBase#compareTo(AbstractBase), so it's not overloading it.
The key point is in understanding why it doesn't work, even when you try to sort() comparing with another Impl, where the more specific signature do matches.
As you defined Comparable<AbstractBase>, you need to define how your
instances compareTo AbstractBase instances. And so you need to implement compareTo(AbstractBase).
You can think that, being Impl a subtype of AbstractBase, the more specific method would be used when a comparison between two Impls takes place. The problem is Java has static binding, and so the compiler defines at compile time which method would use for solving each method call. If what you were doing was sorting AbstractBases, then the compiler would use the compareTo(AbstractBase), that is the one AbstractBase's interface define when it implements the Comparable(AbstractBase) interface.
You can make Impl implement the Comparable<Impl> interface for using the compareTo(Impl) method, but that would only work if you explicitly sort things that are known to be Impls at compile time (ie, an Impl object or Collection<Impl>).
If you really want to apply a different comparison whenever your two objects are Impls, you should fall to some kind of double-dispatch in your Impl#compareTo(AbstractBase) like:
Impl >>>
int compareTo(AbstractBase other) {
return other.compareToImpl(this);
}
int compareToImpl(Impl other) {
// perform custom comparison between Impl's
}
AbstractBase >>>
int compareTo(AbstractBase other) {
// generic comparison
}
int compareToImpl(Impl other) {
// comparison between an AbstractBase and an Impl.
//Probably could just "return this.compareTo(other);", but check for loops :)
}
This requires you add some Impl information in your AbstractBase, which is not pretty, though, but solves the problem the more elegant way it could - using reflection for this is not elegant at all.
The Liskov substitution principle (http://en.wikipedia.org/wiki/Liskov_substitution_principle) states: if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may substitute objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.)
In your case, you are overriding the compareTo method from the Base class in a way that breaks the behaviour of the original method. This is probably why FindBugs has an issue with it.
If you want to be proper about it:
abstract class AbstractBase {
}
class Impl1 extends AbstractBase implements Comparable<Impl1> ...
class Impl2 extends AbstractBase implements Comparable<Impl2> ...
OR
even better, do not use the Comparable interface at all - use a Comparator at sort time instead.
However, in real life there are situations where you need to get around it (maybe you don't have access to the source of AbstractBase, or maybe your new class is just a POC). In these special cases, I would go with the "ugly" cast solution proposed by John.
Why won't the following program compile? Correct it so that it will compile and properly implement Comparable.
class Int implements Comparable
{
private int x;
public Int(int x)
{
this.x = x;
}
public int compareTo(Int other)
{
return x - other.x;
}
}
----//// im assuming that the compareTo method is wrong.. but i dont know why or how to fix it.
The interface Comparable is defined with a type parameter of <T>.
If one does not provide a generic type parameter to a class or interface that requires it, then the default type parameter is assumed to be Object.
So, in effect, your class declaration reads like this:
public class Int implements Comparable<Object>
Now, this is where things get interesting. compareTo(T other) takes the generic type parameter as its type argument. If you don't explicitly declare the type to be something, then the method signature reads compareTo(Object other).
Suffice to say, but Object and Int aren't the same object. When you attempt to declare your method as #Override, the compiler will inform you that your method doesn't inherit or implement anything.
Ultimately, what this boils down to: you have to fix your type parameter. If you want to compare against Int, explicitly declare it so:
public class Int implements Comparable<Int>
Now, your code will compile.
Replace:
class Int implements Comparable
With:
class Int implements Comparable<Int>
You need to specify what you're comparing to. If it's blank, then you need to compare Objects in compareTo().
Try This
public class Int implements Comparable<Int> {
private int x;
public Int(int x) {
this.x = x;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
#Override
public int compareTo(Int other) {
return x-other.getX();
}
}
This is a problem I've been working on for a little while now and I've barely made any progress. I've been running into a lot of problems trying to use an abstract class when extending ArrayList.
My first step was to define three classes, Circle, Rectangle, and Triangle that are all concrete subclasses of an abstract class GeometricObject. The base class has abstract methods getArea() and getPerimeter() that are overridden in the subclasses by the specific getPerimeter() and getArea() formula for that particular object. This part is completed and is working as intended.
The next part is where I'm having trouble. I'm supposed to define a new class GeoemetricObjectList that extends ArrayList<GeometricObject>. This class should override the add(), remove(), and clear() methods. This class keeps a totalArea and totalPerimeter variable of the objects on the list.
Right now I've created a, quite frankly, messy if statement list in my GeometricObjectList add() method that I'd like to clean up. Here is my code for that class so far:
import java.util.ArrayList;
#SuppressWarnings("hiding")
public class GeometricObjectList<GeometricObject> extends ArrayList<GeometricObject>{
final static long serialVersionUID = 1L;
public double totalArea = 0;
public double totalPerimeter = 0;
public boolean add(GeometricObject element){
if(element instanceof Rectangle) {
totalArea += ((Rectangle)element).getArea();
totalPerimeter += ((Rectangle)element).getPerimeter();
}
if(element instanceof Circle) {
totalArea += ((Circle)element).getArea();
totalPerimeter += ((Circle)element).getArea();
}
if(element instanceof Triangle) {
totalArea += ((Triangle)element).getArea();
totalPerimeter +=((Triangle)element).getArea();
}
return super.add(element);
}
public boolean remove(GoemetricObject element) {
return super.remove(element);
}
public void clear() {
}
}
When I simply write totalArea += element.getArea() I get the error "The method getArea() is undefined for the type GeometricObject but in my GeometricObject class I have a public abstract getArea() that is overridden by the getArea() method in each concrete (Triangle, Circle, Rectangle) class.
My next issue is with the remove() method in GeometricObjectList. It looks like this:
public boolean remove(GeometricObject element) {
return super.remove(element);
}
I am getting the error "Name clash: The method remove(GeometricObject) of type GeometricObjectList<GeometricObject> has the same erasure as remove(Object) of type ArrayList but does not override it". I never received this error when creating the add() method.
Any help with this is REALLY greatly appreciated. If there's any more info you need ask for it and I'll have it up in a second!
You really had several different issues - including spelling and hiding,
// Make sure you have these methods in your GeometricObject.
// private static abstract class GeometricObject {
// public abstract double getArea();
// public abstract double getPerimiter();
// }
// Do not use that annotation. It was warning you!
public class GeometricObjectList extends
ArrayList<GeometricObject> {
final static long serialVersionUID = 1L;
public double totalArea = 0;
public double totalPerimeter = 0;
public boolean add(GeometricObject element) {
if (element != null && super.add(element)) {
totalArea += element.getArea(); // add area
totalPerimeter += element.getPerimiter(); // add perimeter
return true;
}
return false;
}
public boolean remove(GeometricObject element) { // Spelling!
if (element != null && super.remove(element)) {
totalArea -= element.getArea(); // subtract area
totalPerimeter -= element.getPerimiter(); // subtract perimeter
return true;
}
return false;
}
public void clear() {
super.clear();
}
}
Well, let's start with first things first.
This class declaration isn't doing what you believe it's doing:
public class GeometricObjectList<GeometricObject> extends ArrayList<GeometricObject>
Java is treating GeometricObject as a type parameter. If you want to bound the generic strictly to only instances of a GeometricObject, change that signature to this:
public class GeometricObjectList<T extends GeometricObject> extends ArrayList<T>
Then Java will identify the type parameter T to only be instances of GeometricObject and its children.
You could also eschew the need for the generic type argument altogether and have your ArrayList tightly bound to GeometricObject without a generic:
public class GeometricObjectList extends ArrayList<GeometricObject>
Next, the signature of add gets to change. Since T is already bound to be an instance of GeometricObject, we don't need to explicitly say that's what we're adding.
public boolean add(T element)
If you elected not to use T, then you would keep your signature the same.
In either case, the casts also become redundant and can go away.
Finally, your remove method isn't overriding the inherited remove - it needs to match the signature as well.
public boolean remove(Object element)
I'm looking through some interfaces at the moment and I'm wondering why this does not work:
interface I {
public void doSomething(String x);
}
class MyType implements I {
public int doSomething(String x) {
System.out.println(x);
return(0);
}
}
Basically, why can't I implement the method in the interface? THey have different signatures as one has a return type? Isn't the name, parameters and return type what make a method unique?
You can't have different return types. Imagine the following
class Foo implements I {
public int doSomething(String x) {
System.out.println(x);
return(0);
}
}
class Bar implements I {
public void doSomething(String x) {
System.out.println(x);
return;
}
}
List<I> l = new ArrayList();
l.add(new Foo());
l.add(new Bar());
for (I i : l) {
int x = i.doSomething(); // this makes no sense for Bar!
}
Therefore, the return types must also be the same!
Yeah, you're basically correct. Java doesn't allow overloading methods by return type, which would be neat. However, the interface return type must still match.
The method signature consists of the method's name and the parameters types, so you can't declare more than one method with the same name and the same number and type of arguments, because the compiler cannot tell them apart.
Think of a typical use for interfaces: e.g. anything implementing the java List interface must implement boolean add(Object o)
The caller is probably going to do something like:
if (!impl.add(o)) { /* report error */ }
If you were allowed to change the return type, you'd hit all types of problems.
void add(Object o)
if (!impl.add(o)) { // ... your method returns void, so this makes no sense
float add(Object o)
if (!impl.add(o)) { // float to boolean ? are you sure that is what you meant?