简体   繁体   中英

Why can I cast an object? Can it be done with other objects?

So I was writing a paintComponent(Graphics g) method and I realized at the beginning that I am casting it to Graphics2D g2 = (Graphics2D) g; . I wrote this code a while ago and just realized I have no clue what this is. I googled a bit but object casting is a bit foreign to me.

One object reference can be typecast into another object reference. This is called casting object.

^^Word for word from my textbook by Y. Daniel Liang

I don't understand why this works. Graphics and Graphics2D are two different objects how could they inherit instance variables and methods from each-other? Obviously I am aware of primitive casting ie widening and narrowing. That makes sense since they are just wrapper classes of Integer, Double, Byte etc...

With casting no object conversion or transformation of any kind is happening. Just imagine you have the following class structure:

class Mammal { }
class Human extends Mammal { }
class Dog extends Mammal { }

Now when you create a new instance of Human with Human human = new Human(); that will also be a Mammal, right? So you could write a method like:

public void doSoemthing(Mammal mammal) {
    if (mammal instanceof Human) {
        Human human = (Human) mammal;
        human.doWork();
    } else if (mammal instanceof Dog) {
        Dog dog = (Dog) mammal;
        dog.bark();
    }
}

and invoke the method like:

doSomething(new Human());

So your method can take any type of Mammal as an input parameter and in your method you can check what kind of Mammal it really is. So when you pass a new Human() as the input, the object's actual type will be Human. The reason because you can pass a Human to a method expecting a Mammal is because inheritance. So what your method will know is that the input parameter is definitely a Mammal. But it can be any kind of Mammal. If you want to know what the Object's actual type is you can use instanceof for example. And in this line:

Human human = (Human) mammal;

doesn't mean you are converting the mammal input parameter. It just means that from now on you want to use the input parameter as a Human. And you can do that, because with the isntanceof you check that it's really a Human. You could also do something like this:

public void doExample2(Object input) {
    if (input instanceof Integer) {
        System.out.println("I am a number");
    } else if (input instanceof Graphics) {
        System.out.println("I am a Graphics");
    }
}

Notice the type of teh input parameter. Object is the base class of everything.

So getting back to your scenario. You do that casting, because from the context of your application the input parameter will always be a Graphics2D, and that's why you can do that casting, and also to use the methods provided by Graphics2D.

Both Graphics2D and Graphics are abstract classes, which means you can't have an instance of them, but you can have an object which extends one of them.

Graphics2D extends Graphics , so every object that extends Graphics2D will extend Graphics as well, but it doesn't mean every object that extends Graphics can be cast to Graphics2D .

abstract class Graphics {}

abstract class Graphics2D extends Graphics {}

class ClassGraphics extends Graphics {}

class ClassGraphics2D extends Graphics2D {}

class Main {

    public static void main(String[] args) {
        Graphics g1 = new ClassDebugGraphics();
        Graphics g2 = new ClassDebugGraphics2D();
        paintComponent(g1);
        paintComponent(g2);
    }

    public static void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        System.out.print("Successful casting");
    }
}

In the above example both g1 and g2 are Graphics , but only g2 is Graphics2D .

paintComponent(g1); will throw java.lang.ClassCastException: ClassDebugGraphics cannot be cast to Graphics2D , while paintComponent(g2); will print Successful casting .

You didn't mention what is sent to paintComponent() in your code, but it extends Graphics2D so you can cast it to it.

If you look here " https://docs.oracle.com/javase/7/docs/api/java/awt/Graphics2D.html "

In the top you will see java.lang.Object -> java.awt.Graphics -> java.awt.Graphics2D

So what does it mean!?: java.lang.Object is the super class for booth Graphics and Graphics2D. Graphics is super class for Graphics2D. So Graphics2D extends Graphics, therefore they are not so "different" objects.

If you have a look at the Javadoc, you see, that java.awt.Graphics2D is a subclass of java.awt.Graphics . https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/java/awt/Graphics2D.html

In Java 11 the direct known subclasses of Graphics are DebugGraphics and Graphics2D , so when you have a Graphics g , it is most probably also a Graphics2D , since Graphics itself is an abstract class and thus, cannot be instantiated directly. So you can cast the object to the specific type. To be sure, you could do check first, like so.

if (g instanceof Graphics2D) {
    Graphics2D g2 = (Graphics2D) g;
    ...
}

The cast is probably done, in order for you to be able to access methods, defined in Graphics2D , but not in Graphics .

Another look at the Javadoc will reveals, that Graphics2D also is an abstract class. So, what you can do, to see, what class you are really dealing with is, print out the class name like this:

System.out.println(g.getClass().getName());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM