Java 为什么我可以投射一个对象?可以用其他对象来完成吗?

Java 为什么我可以投射一个对象?可以用其他对象来完成吗?,java,casting,Java,Casting,因此,我正在编写一个paintComponent(Graphics g)方法,我一开始就意识到我正在将它转换为Graphics2D g2=(Graphics2D)g。我不久前写了这段代码,才意识到我不知道这是什么。我在谷歌上搜索了一下,但对象投射对我来说有点陌生 一个对象引用可以类型转换为另一个对象引用。这称为铸造对象 ^^从我的课本中逐字逐句地读出来,作者是Y.Daniel Liang 我不明白为什么会这样Graphics和Graphics2D是两个不同的对象,它们如何从彼此继承实例变量和方法

因此,我正在编写一个
paintComponent(Graphics g)
方法,我一开始就意识到我正在将它转换为
Graphics2D g2=(Graphics2D)g。我不久前写了这段代码,才意识到我不知道这是什么。我在谷歌上搜索了一下,但对象投射对我来说有点陌生

一个对象引用可以类型转换为另一个对象引用。这称为铸造对象

^^从我的课本中逐字逐句地读出来,作者是Y.Daniel Liang

我不明白为什么会这样
Graphics
Graphics2D
是两个不同的对象,它们如何从彼此继承实例变量和方法?很明显,我意识到了原始铸造即加宽和变窄。这是有道理的,因为它们只是整数、双精度、字节等的包装类。

如果你看这里“

在顶部,您将看到java.lang.Object->java.awt.Graphics->java.awt.Graphics2D


这是什么意思!?:Object是booth Graphics和Graphics2D的超类。Graphics是Graphics2D的超级类。因此Graphics2D扩展了图形,因此它们不是那么“不同”的对象。

Graphics2D
Graphics
都是抽象类,这意味着不能有它们的实例,但可以有一个扩展其中一个的对象

Graphics2D
扩展了
Graphics
,因此扩展
Graphics2D
的每个对象也将扩展
Graphics
,但这并不意味着扩展
Graphics
的每个对象都可以强制转换为
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");
    }
}
在上述示例中,
g1
g2
都是
图形
,但只有
g2
图形2d

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");
    }
}
paintComponent(g1)
将抛出
java.lang.ClassCastException:ClassDebugGraphics无法转换为Graphics2D
,而
paintComponent(g2)将打印
成功铸造


您在代码中没有提到发送到
paintComponent()
的内容,但它扩展了
Graphics2D
,因此您可以将其强制转换为它。

通过强制转换,不会发生任何类型的对象转换或转换。 假设您有以下类结构:

class Mammal { }
class Human extends Mammal { }
class Dog extends Mammal { }
现在,当您使用
Human-Human=new-Human()创建一个新的Human实例时
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();
    }
}
doSomething(new Human());
并调用如下方法:

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();
    }
}
doSomething(new Human());
因此,您的方法可以将任何类型的哺乳动物作为输入参数,在您的方法中,您可以检查它到底是什么类型的哺乳动物。因此,当您传递一个
new Human()
作为输入时,对象的实际类型将是Human。之所以可以将人类传递给预期为哺乳动物的方法,是因为继承。因此,您的方法将知道,输入参数肯定是哺乳动物。但它可以是任何种类的哺乳动物。如果您想知道对象的实际类型,可以使用
instanceof
作为示例。在这方面:

Human human = (Human) mammal;
并不意味着要转换哺乳动物输入参数。这只是意味着从现在起,您希望将输入参数用作人。你可以这么做,因为用IsNanceof检查它是否真的是人类。你也可以这样做:

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");
    }
}
请注意输入参数的类型。对象是所有事物的基类


回到你的场景。您可以进行这种转换,因为从应用程序的上下文来看,输入参数始终是Graphics2D,这就是为什么您可以进行这种转换,也可以使用Graphics2D提供的方法。

如果您查看Javadoc,您会发现,
java.awt.Graphics2D
java.awt.Graphics
的子类

在Java 11中,
Graphics
的直接已知子类是
debugraphics
Graphics2D
,因此当您有
Graphics g
时,它很可能也是
Graphics2D
,因为
Graphics
本身是一个抽象类,因此不能直接实例化。因此可以将对象强制转换为特定类型。当然,你可以先检查一下,就像这样

if(图形的g实例2d){
图形2d g2=(图形2d)g;
...
}
为了让您能够访问在
Graphics2D
中定义的方法,而不是在
Graphics
中定义的方法,可能需要执行强制转换

再看看Javadoc就会发现,
Graphics2D
也是一个抽象类。所以,你能做的是,看看你真正处理的是什么类,打印出类名,如下所示:

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");
    }
}
System.out.println(g.getClass().getName());

同一对象既可以是
图形
也可以是
图形2D
。关键是类
Graphics2D
扩展了类
Graphics
。该强制转换之所以有效,是因为该对象始终是
Graphics2D
的子类型。不,它们没有完全不同,Graphics2D扩展了图形!因此Graphics2D将拥有所有公开的图形方法,如果该方法没有接收到Graphics2D对象,它应该抛出一个错误。这并不能解释编译器实际上是如何解释强制转换的。就像我说的,我显然理解原始铸造。所以从Graphics到Graphics2D的转换是有意义的。因为转换没有抛出异常,这意味着此时“g”是Graphics2D的实例,否则转换会失败。我的代码是
paintComponent(Graphics g)
,我不发送任何内容,当使用
super.paintComponent(g)创建JFrame时会调用它在我覆盖的组件内。所以它不是发送一个Graphics2D,而是一个图形对象……对于可能出现的任何混乱,我深表歉意caused@ErlichBachman
Graphics
是超类,
paintComponent
接收此类型作为参数,