Java继承和方法解析顺序

3

我有以下代码示例:

class p {
    public void druckauftrag() {
        // ...
        drucke();
    }

    public void drucke() {
        System.out.println("B/W-Printer");
    }
}

class cp extends p {
    public void drucke() {
        System.out.println("Color-Printer");
    }
}

以下代码:

  cp colorprinter = new cp();
  cp.druckauftrag();

我理解为什么"cp.druckauftrag();"会在控制台输出"Color-Printer",这没有问题。

但当我调用:

    p drucker = (p)colorprinter;
    drucker.druckauftrag();

我得到了相同的输出 - 为什么呢?

类型转换是否会覆盖对象“drucker”的方法“drucke”,使用来自colorprinter的“drucke”?

提前感谢您的每一个解释。


1
一个小建议:为了清晰明了,请使用大写字母来命名你的类。 - Aleksandar Stefanović
1
永远不要在命名类、方法等时使用非英语母语。请使用英文驼峰式命名法。 - Grzegorz Górkiewicz
类类型是定义对象如何被“外部用户”(其他类)看待的定义。方法实现绑定到一个实例,类型仅定义其外部契约。 - topr
3个回答

4
colorprinter在使用强制类型转换时仍然是cp的一个实例,因此它对public void drucke()方法的实现不会改变。 您使用(p)colorprinter强制类型转换所表达的是您期望对象colorprinter满足的合同(接口),其中包括具有public void drucke()签名的公共方法,但没有任何特定的实现。
顺便说一下,当您声明类型为pdrucker时,这种强制类型转换已经隐式执行,因此p drucker = (p)colorprinter;中的(p)是多余的。 p drucker = colorprinter;就足够了。 在这里您可以了解更多关于类型转换的信息
请记住,最佳实践是从抽象类或接口扩展,并且只有实现抽象方法@Override。您的代码设计更好的方式是:
abstract class BasePrinter {

    public void druckauftrag() {
        // ...
        drucke();
    }

    public void drucke();

}

class p extends BasePrinter {    
    public void drucke() {
        System.out.println("B/W-Printer");
    }
}

class cp extends BasePrinter {
    public void drucke() {
        System.out.println("Color-Printer");
    }
}

当然,限制条件并不总是允许进行这种重新设计。将基本要求作为参数传递给构造函数(依赖注入)而不是扩展基类也可以是一个很好的选择:

interface Druckable {
    void drucke();
}

class Druckauftrager {

    Druckable dk;
    Druckauftrager(Drukable dk){
        this.dk = dk;
    }
    public void druckauftrag() {
        // ...
        dk.drucke();
    }

}

class p implements Druckable {    
    public void drucke() {
        System.out.println("B/W-Printer");
    }
}

class cp implements Druckable {
    public void drucke() {
        System.out.println("Color-Printer");
    }
}

如果你想表达一台打印机需要或可以有多个打印功能(比如彩色和黑白),你只需编写带有足够多的Drukable属性和构造函数参数的类,例如:

class BlackAndWhiteOrColorPrinter {

    p blackAndWhitePrintService;
    cp colorPrintService;

    Druckable selectedPrintService;

    BlackAndWhiteOrColorPrinter (p blackAndWhitePrintService, cp colorPrintService){
        this.blackAndWhitePrintService = blackAndWhitePrintService;
        this.colorPrintService = colorPrintService;
        this.selectedPrintService = blackAndWhitePrintService;
    }

    public void druckauftrag() {
        // ...
        selectedPrintService.drucke();
    }

}

通过创建一个具有MultiPrinter(List<Druckable> printServices)构造函数的class MultiPrinter,并将任意数量的打印模式添加到其打印服务列表中:pcp和以后可能出现的Druckable实现及其public void drucke()方法。如果您想引入单元测试,那么这种方法也非常实用,因为您可以提供模拟对象来强制执行您想要测试的特定条件,例如druke()抛出PaperJamException

有关接口、覆盖和继承的更多信息,请参见https://docs.oracle.com/javase/tutorial/java/IandI/usinginterface.html

顺便说一下,根据官方最新修订的Java代码规范指南和事实标准,Java中的类应使用驼峰命名法。在所有定义中使用语义化命名也会大大受益,例如BlackAndWhitePrinter blackAndWhitePrinterColorPrinter colorPrinter


3

colorprintercp 的一个实例。即使你将其向上转换为 p,它的 drucke() 方法仍然来自于 cp

不同之处在于,当你向上转型为 p 后,就无法调用 cp 自己定义的方法了。


0
当您使用new运算符创建对象时,内存将在中分配。方法和字段实际上取决于对象的具体实际类。 如果子类覆盖并修改了其超类的行为,则调用被覆盖的方法将始终导致修改后的行为。强制转换只意味着子类对象现在由超类型表示,因为该对象对于方法的修改行为将始终导致修改后的行为。
假设您有以下类
public class Fruit{
   public void taste(){
     System.out.println("depends upon the actual fruit"); 
   }
}

public class Mango extends Fruit{
   @Override
   public void taste(){
     System.out.println("sweet"); 
   }
   public void wayToExposeSuperMethod(){
     super.taste();
   }
}

换句话说,这就像把mango称为fruit,但mango仍然是mango。 对于上面的代码

Fruit fruit = new Mango();

fruit.taste(); // <-- this will output : sweet

((Mango)fruit).taste();// <-- this will output : sweet

fruit.wayToExposeSuperMethod(); // <-- this will not compile

((Mango)fruit).wayToExposeSuperMethod(); // <-- this will output : depends upon the actual fruit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接