Java继承和方法解析顺序
Java inheritance and method resolution order
我有以下代码示例:
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"?
提前感谢您的每一个解释。
colorprinter
是 cp
的实例。即使你将它向上转换为 p
,它的 drucke()
方法仍然是来自 cp
.
的方法
不同的是,在你upcast colorprinter
之后,你将无法调用cp
自己定义的方法。
colorprinter
不会停止成为 cp
的一个实例,当你在它上面使用 cast 运算符时 ,所以 它的实现public void drucke()
不变
你用 (p)colorprinter
转换表达的是你希望对象 colorprinter
满足的契约(接口)类型,其中包括一个 public 方法,签名为 public void drucke()
,但没有具体实现。
而且,顺便说一句,当您声明 p
类型的 drucker
时,此转换已经隐式执行,因此 (p)
在 p drucker = (p)colorprinter;
中是多余的。 p drucker = colorprinter;
就足够了。
Here you can learn more about typecasting。
请记住,最好的做法是从抽象 class es 或接口和仅 @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) 而不是扩展基础 class 也是一个不错的选择:
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");
}
}
现在,如果您想表达打印机需要或可以具有多种打印功能(如颜色和 b/w),您只需编写具有尽可能多的额外 Drukable 属性的 class 和构造函数参数随心所欲,例如:
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
,并向其打印服务列表添加任意数量的打印模式:p
、cp
,以及未来 Druckable
及其 public void drucke()
的任何其他实现。如果您想引入单元测试,它也非常实用,因此您可以提供模型对象来强制执行您想要测试的特定条件,例如 druke()
抛出 PaperJamException
。
有关接口、覆盖和继承如何工作的更多信息,请参阅 https://docs.oracle.com/javase/tutorial/java/IandI/usinginterface.html
顺便说一句,根据官方 java code conventions guide and also by de facto standard, classes in Java should use CamelCase 命名约定的最新修订。您还可以从对所有定义使用语义命名中受益匪浅,例如 BlackAndWhitePrinter blackAndWhitePrinter
和 ColorPrinter colorPrinter
.
当您使用 new
运算符创建对象时,内存分配在 heap
中。方法和字段实际上取决于对象的具体实际 class。
改变子 class 覆盖和修改其超级 class 的行为,调用被覆盖的方法将始终导致修改后的行为。转换仅意味着 sub class 的对象现在由超类型表示,因为该对象具有修改后的行为,方法将始终导致修改后的行为。
假设您有以下 classes
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
我有以下代码示例:
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"?
提前感谢您的每一个解释。
colorprinter
是 cp
的实例。即使你将它向上转换为 p
,它的 drucke()
方法仍然是来自 cp
.
不同的是,在你upcast colorprinter
之后,你将无法调用cp
自己定义的方法。
colorprinter
不会停止成为 cp
的一个实例,当你在它上面使用 cast 运算符时 ,所以 它的实现public void drucke()
不变
你用 (p)colorprinter
转换表达的是你希望对象 colorprinter
满足的契约(接口)类型,其中包括一个 public 方法,签名为 public void drucke()
,但没有具体实现。
而且,顺便说一句,当您声明 p
类型的 drucker
时,此转换已经隐式执行,因此 (p)
在 p drucker = (p)colorprinter;
中是多余的。 p drucker = colorprinter;
就足够了。
Here you can learn more about typecasting。
请记住,最好的做法是从抽象 class es 或接口和仅 @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) 而不是扩展基础 class 也是一个不错的选择:
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");
}
}
现在,如果您想表达打印机需要或可以具有多种打印功能(如颜色和 b/w),您只需编写具有尽可能多的额外 Drukable 属性的 class 和构造函数参数随心所欲,例如:
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
,并向其打印服务列表添加任意数量的打印模式:p
、cp
,以及未来 Druckable
及其 public void drucke()
的任何其他实现。如果您想引入单元测试,它也非常实用,因此您可以提供模型对象来强制执行您想要测试的特定条件,例如 druke()
抛出 PaperJamException
。
有关接口、覆盖和继承如何工作的更多信息,请参阅 https://docs.oracle.com/javase/tutorial/java/IandI/usinginterface.html
顺便说一句,根据官方 java code conventions guide and also by de facto standard, classes in Java should use CamelCase 命名约定的最新修订。您还可以从对所有定义使用语义命名中受益匪浅,例如 BlackAndWhitePrinter blackAndWhitePrinter
和 ColorPrinter colorPrinter
.
当您使用 new
运算符创建对象时,内存分配在 heap
中。方法和字段实际上取决于对象的具体实际 class。
改变子 class 覆盖和修改其超级 class 的行为,调用被覆盖的方法将始终导致修改后的行为。转换仅意味着 sub class 的对象现在由超类型表示,因为该对象具有修改后的行为,方法将始终导致修改后的行为。
假设您有以下 classes
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