1
public interface Interface1 {
    void methodFromInterface1();    
}
public interface Interface2 {    
    void methodFromInterface2();    
}
public class Superclass implements Interface1, Interface2 {

    @Override
    public void methodFromInterface1() {
        System.out.println("Called methodFromInterface1");
    }

    @Override
    public void methodFromInterface2() {
        System.out.println("Called methodFromInterface2");
    }    
}

public class Testclass {    
    @Test
    public void test() {
        
        Interface1 interface1 = new Superclass();
        
        if (interface1 instanceof Interface1) {
            System.out.println("superclass instance of interface1");
            interface1.methodFromInterface1();
        }
        if (interface1 instanceof Interface2) {
            System.out.println("superclass instance of interface2");
            ((Interface2)interface1).methodFromInterface2();
        }        
    }    
}

This prints out:

superclass instance of interface1
Called methodFromInterface1
superclass instance of interface2
Called methodFromInterface2
superclass instance of interface1
Called methodFromInterface1
superclass instance of interface2
Called methodFromInterface2

In the Testclass. Superclass is casted to interface1, so the interface1 variable is an Interface1 type. How is it possible that Java allows me to cast the interface1 to a Interface2 object and then call the methodFromInterface2? It should have no information about Interface2 when going from Interface1.

6
  • Are you asking why this is allowed at compile time, or why this is allowed at runtime? Commented Feb 7, 2023 at 7:07
  • Obviously, the compiler has access to both the definitions of Interface1 and Interface2. Now the compiler may generate instructions to search for Interface2 at runtime. Commented Feb 7, 2023 at 7:09
  • 3
    I'm not sure I understand your confusion. The Superclass class implements both interfaces. Instances of that class are instances of both interfaces. Casting does not change the type of the object. It makes the compiler see a variable as a different type. Casting is essentially a way to say, "I know better than the compiler". Commented Feb 7, 2023 at 7:16
  • @Sweeper It's a runtime question :) Commented Feb 7, 2023 at 7:18
  • Does this answer your question? Java casting in interfaces Commented Feb 7, 2023 at 7:18

3 Answers 3

2

In Java, you can do all kinds of casts even between seemingly unrelated types. There is a runtime check if the object that is referenced can be actually cast to that, and if not, you'll get a ClassCastException.

https://docs.oracle.com/javase/specs/jls/se11/html/jls-5.html#jls-5.1.6.3

Note that the things considered are R - the actual type of the value and T - the type you are casting to. The apparent type you are casting from is not relevant.

Sign up to request clarification or add additional context in comments.

2 Comments

"The apparent type you are casting from is not relevant" Did I misunderstand this? The type you're casting from does matter. If it's a final class, the compiler can make assertions about casts that are definitely going to fail. ideone.com/hC1wGA
@Michael I'm oversimplifying a fair bit there to address the case presented in the question. To achieve a completely accurate statement, we would have to end up with a copy of the relevant bits of the spec. Which I would encourage people to read instead of my inaccurate interpretation.
0

This is my view of classes, extending a class, implementing interfaces and variabels to store objects in.

Variables as hooks (coathangers)

Say you have a square hook, you can only hang square objects on it. On a circular hook you can only hang circular objects. Since there are a lot of different classes in Java there are a lot of different shapes on the hooks. So let us just use words instead of shapes to describe a hook. Object is a class and therefore a hook shape. Superclass is another. Interfaces are also hook shapes. We have Interface1 and Interface2.

Superclass extends Object by default so we can say the definition is class Superclass extends Object implements Interface1, Interface2. This means it can be hanged on 4 different hooks, or 4 different variables. The following compiles:

Superclass superclass = new Superclass();
Object object = new Superclass();
Interface1 interface1 = new Superclass();
Interface2 interface2 = new Superclass();

The compiler knows what Superclass looks like and allows it to be stored in any of the variables.

But what happens if we take an object from one hook and places it on another? The compiler only knows the shape of the source hook, not of the object actually hanging there. That information is only available at runtime. The following will compile:

object = superclass;
interface1 = superclass;
interface2 = superclass;

The following will not compile:

interface2 = interface1;

The compiler only sees Interface1 and Interface2 and knows they are not same. At runtime interface1 actually contains a Superclass so we know it fits on the hook. We can override the compiler with a cast:

interface2 = (Interface2) interface1;

This compiles and works at runtime.

If we add another class in the mix: class Anotherclass implements Interface1 (it also extends Object). And we do the same thing when assigning to a variable of type Interface2, which the class don't implement:

Anotherclass anotherclass = new Anotherclass();
Interface1 interface1 = anotherclass;
Interface2 interface2 = (Interface2) interface1;

We think interface1 contains an object that fits in interface2 and overrides the compiler. This will however throw a ClassCastException at runtime because the object in the source variable don't implement Interface2.

Comments

-1

You missed the point, and maybe about superclasses. An interface is "abstract" meaning it has no unique instance, it is never created "new"(instance)! An "abstract class" can only have a non argument constructor and no global variables (can have static fields because they are not unique), so it is only a shell with "no instance" , it is not designed to be new (not unique)! An abstract class has some abstract methods (and usually many methods that have working body code too), method signatures that have no body code, "interfaces" ONLY have abstract methods. For an abstract method in an abstract class to operate, the working body code must be written in a "class" that the abstract class is extended from, with that signature and also has body code, a class must extend from the abstract class containing a method with working body code of that method signature (basically method overloading). Of interfaces, A class must implement the interface and the class must have the method signature with working code to call from an interface reference of the interface method. So a class will implement an interface, or a class extends from an abstract class with that interface, has the working code and signature of the method, the class instance can then be cast to the interface to call the method as that type as an interface. (Then there is also default class hierarchy method signature auto search in the JVM runtime but that is something for another day).

The following is why an instantiated object can be cast to an abstract object such as an interface or abstract class making the abstract object thats' been "cast to" the superclass...

Abstract objects are designed to join onto other objects, abstract objects are designed to be dependent on other objects instantiated, not as independent! So, because anything "abstract" cannot be an instance, it cannot have unique values that make it different to another discriminate such as an instance (is why static class fields are allowed in abstract classes and interfaces), not so much as different "AKA valueOf" the same object type T , an abstract object can be cast onto anything else and (just for the point) somewhat uselessly although there are no matching signatures to use, but don't call any and it would sit there without doing anything. (There are ways of abstract classes being able to be "new" instances but that's for another day also)

6 Comments

"An 'abstract class' can only have a non argument constructor and no global variables and no global class fields" -- That's either poorly worded or just incorrect. An abstract class can have any number of constructors with any number of parameters (though obviously no duplicate signatures). They can also have any number of fields, both instance fields and static fields (not sure what you mean by "global" here). And they can have non-abstract methods, too. That said, an interface cannot declare any constructors nor any instance fields.
I know that but I suggest you remove your comment because for now that would be too much for his point. NB and while you were commenting I been editing hard for the simplification for him! Some of javax.swing is abstract but exclusively instantiated, base64 or tomcat base64 is abstract has class fields! He doesn't want to "understand" that this moment!!!
Providing incorrect information, even if tangential to the main point, benefits no one—or worse, actively harms further understanding. Given your comment, and what I understand to be the main point of your answer, I'm not sure I understand why you mentioned constructors or fields in the first place.
Also, interfaces do not have only abstract methods. Since Java 8, interfaces can have default methods, which are concrete methods.
I'm completely aware of that Mark, it's not needed (default method) at this point to explain the concept, only the T typing and the java OOP concept of "abstract".
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.