我有以下代码示例:
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();
我得到相同的输出 - 为什么? 类型转换是否用colorprinter中的“drucke”覆盖了对象“drucker”的方法“drucke”?
提前感谢您的每一个解释。
答案 0 :(得分:4)
colorprinter
在您使用强制转换操作符时不会停止成为cp
的实例,因此 public void drucke()
的实现不会变化强>
您使用(p)colorprinter
投射表达的内容是您期望对象colorprinter
满足的合同(界面),其中包含带有签名public void drucke()
的公共方法,但是没有任何具体的实施。
顺便说一句,当您声明drucker
类型的p
时,此投射已经隐式执行,因此(p)
中的p drucker = (p)colorprinter;
是多余的。{1}}。 p drucker = colorprinter;
就足够了。
Here you can learn more about typecasting。
请记住,从抽象类或接口扩展并且只有@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");
}
}
但当然,限制并不总是允许这种重新设计。将基本需求作为参数传递给构造函数(dependency injection)而不是扩展基类也是一个很好的选择:
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();
}
}
这样,您甚至可以使用class MultiPrinter
构造函数编写MultiPrinter(List<Druckable> printServices)
,并将任意数量的打印模式添加到其打印服务列表中:p
,cp
,以及Druckable
及其public void drucke()
的其他任何实现都会在将来出现。如果您想引入单元测试,这也是非常实用的,因此您可以提供强制要测试的特定条件的模型对象,例如druke()
抛出PaperJamException
。
有关接口,覆盖和继承如何工作的更多信息,请参阅https://docs.oracle.com/javase/tutorial/java/IandI/usinginterface.html
BTW,根据官方java code conventions guide的最新修订版以及事实上的标准,Java中的类应该使用CamelCase命名约定。您还可以通过在所有定义上使用辅助命名来获益,例如BlackAndWhitePrinter blackAndWhitePrinter
和ColorPrinter colorPrinter
。
答案 1 :(得分:3)
colorprinter
是cp
的一个实例。即使您将其向上转换为p
,它的drucke()
方法仍然是来自cp
的方法。
不同之处在于,在您向上转换colorprinter
之后,您将无法调用cp
自己定义的方法。
答案 2 :(得分:0)
使用new
运算符创建对象时,内存将在heap
中分配。实际上,方法和字段取决于对象的具体实际类。
更改子类覆盖并修改其超类的行为,调用重写的方法将始终导致修改的行为。转换只会意味着子类的对象现在由超类型表示,因为对象具有修改的方法行为将始终导致修改后的行为。
假设您有以下类
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