Abhinav Arora
Abhinav Arora

Reputation: 537

Java: Interface

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

Upvotes: 0

Views: 655

Answers (6)

Pablo Lozano
Pablo Lozano

Reputation: 10342

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();

}

Upvotes: 1

Bartek Maraszek
Bartek Maraszek

Reputation: 1414

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

Upvotes: 1

dertoni
dertoni

Reputation: 1803

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.

Upvotes: 0

asthasr
asthasr

Reputation: 9397

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.

Upvotes: 0

Sanjeev Kumar
Sanjeev Kumar

Reputation: 660

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.

Upvotes: 0

Tassos Bassoukos
Tassos Bassoukos

Reputation: 16142

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.

Upvotes: 0

Related Questions