调用超类的方法,通过传递一个子类的实例,期待在子类实例上有超类参数
任何人都可以解释以下代码的输出,这里涉及的Java原则是什么?
class Mammal {
void eat(Mammal m) {
System.out.println("Mammal eats food");
}
}
class Cattle extends Mammal{
void eat(Cattle c){
System.out.println("Cattle eats hay");
}
}
class Horse extends Cattle {
void eat(Horse h) {
System.out.println("Horse eats hay");
}
}
public class Test {
public static void main(String[] args) {
Mammal h = new Horse();
Cattle c = new Horse();
c.eat(h);
}
}
它会产生以下输出:
Mammal eats food
我想知道我们如何以上述结果进行。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
超载与覆盖
不是有效的 ,因为所有方法签名(方法名称 +参数)是不同的:
这就是称为 方法过载 ( 请参阅 )类
马
将具有3
不同的方法,而不是一种。即其自己的超载版本的eat()
和2
继承版本。编译器将将方法调用
C.EAT(H)
映射到最特定的方法,即eat(Mammal M)
,因为变量h 是类型
哺乳动物
。为了用签名
eat(Horse H)
调用该方法,您需要将h
施加到类型Horse
中。请注意,这种转换将被视为所谓的 缩小转换 ,它永远不会自动发生,因为不能保证这种类型的演员会成功,因此编译器不会为您做。评论该方法
void Eat(Mammal M)
,您将看到编译错误 - 编译器不会执行缩小转换,它只能帮助您使用 /a>,因为它们可以保证成功并因此安全。如果您要手动进行类型铸造,将会发生什么:
胁迫
h
进入类型horse
:output:,
因为变量
c
c
/code>是类型
牛
它仅知道方法eat(cattle c)
而不是eat(Horse H)
。在幕后,编译器将 将h
延伸到类型牛
。强迫
C
和H
进入类型马
:输出:
覆盖
方法规则 的规则覆盖符合 liskov liskov替代原则 。
儿童班级应以这种方式声明其行为,以便可以在期望其父母的任何地方使用它:
方法签名必须与完全匹配。 IE 方法名称应与参数的类型相同。并且需要按相同的顺序声明参数。重要的是要注意,如果方法签名有所不同(f 或类似于问题中提供的代码段中的实例,其中一种方法的名称已拼写错误),则编译器不知道这些方法是无论如何连接。即,不再认为它是覆盖的案例,方法将被视为不同,并且下面列出的所有其他要求将不适用。这就是为什么强烈建议将
@override
注释添加到覆盖方法的原因。有了它,当编译器未能在父类和接口中找到匹配方法时,如果您拼写了名称或以错误的顺序声明参数,则将提供明确的反馈。如果您要求它生成模板(Intellij ctrl> ctrl + o o 注释 >)。访问修饰符被覆盖方法的可以是相同的或更宽的,但不能更严格。 IE
可以将父类中的受保护
方法覆盖为public
,也可以保留受保护
,我们无法将其制作private> private
。在原始类型的情况下,被覆盖方法的返回类型应完全相同。但是,如果父方法声明返回参考类型,则可以返回其子类型。即,如果父母返回
号码
覆盖方法可以提供整数
作为返回类型。如果父级方法声明要投掷任何检查的异常,则允许覆盖方法 可以声明相同的 exceptions 或他们的子类型,也可以可以将其实现为安全( ie根本不抛出异常)。不允许使覆盖方法 不如父母所声明的方法,即抛出检查的例外未通过父方法声明。 注释,关于运行时异常没有限制(未检查),即使未通过父方法指定它们,也可以自由声明它们。
这将是方法覆盖的有效示例:
main()
输出:
Overloading vs Overriding
That's not a valid method overriding, because all the method signatures (method name + parameters) are different:
That is called method overloading (see) and class
Horse
will have3
distinct methods, not one. I.e. its own overloaded version ofeat()
and2
inherited versions.The compiler will map the method call
c.eat(h)
to the most specific method, which iseat(Mammal m)
, because the variableh
is of typeMammal
.In order to invoke the method with a signature
eat(Horse h)
you need to coerceh
into the typeHorse
. Note, that such conversion would be considered a so-called narrowing conversion, and it will never happen automatically because there's no guarantee that such type cast will succeed, so the compiler will not do it for you.Comment out the method
void eat(Mammal m)
and you will see the compilation error - compilers don't perform narrowing conversions, it can only help you with widening conversions because they are guaranteed to succeed and therefore safe.That what would happen if you'll make type casting manually:
Coercing
h
into the typeHorse
:Output:
Because variable
c
is of typeCattle
it's only aware of the methodeat(Cattle c)
and noteat(Horse h)
. And behind the scenes, the compiler will widen theh
to the typeCattle
.Coercing both
c
andh
into the typeHorse
:Output:
Rules of Overriding
The rules of method overriding conform to the Liskov substitution principle.
The child class should declare its behavior in such a way so that it can be used everywhere where its parent is expected:
Method signatures must match exactly. I.e. method names should be the same as well as the types of parameters. And parameters need to be declared in the same order. It is important to note that if method signatures differ (for instance like in the code snippet provided in the question, name of one of the methods was misspelled) the compiler will have no clue that these methods are connected anyhow. I.e. it no longer be considered a case of overriding, methods will be considered to be distinct, and all other requirements listed below will not be applicable. That's why it's highly advisable to add the
@Override
annotation to the overridden method. With it, the compiler will give a clear feedback when it fails to find a matching method in the parent classes and interfaces, if you've misspelled the name, or declared parameters in the wrong order. Your IDE will add this annotation for you if you ask it to generate a template (shortcut in IntelliJ CTRL + O).The access modifier of an overridden method can be the same or wider, but it can not be more strict. I.e.
protected
method in the parent class can be overridden aspublic
or can remainprotected
, we can not make itprivate
.Return type of an overridden method should be precisely the same in case primitive type. But if a parent method declares to return a reference type, its subtype can be returned. I.e. if parent returns
Number
an overridden method can provideInteger
as a return type.If parent method declares to throw any checked exceptions then the overridden method is allowed to declare the same exceptions or their subtypes, or can be implemented as safe (i.e. not throwing exceptions at all). It's not allowed to make the overridden method less safe than the method declared by the parent, i.e. to throw checked exceptions not declared by the parent method. Note, that there are no restrictions regarding runtime exceptions (unchecked), overridden methods are free to declare them even if they are not specified by the parent method.
This would be a valid example of method overriding:
main()
Output:
在您的示例中,发生(相同的方法名称,但传递了不同的参数类型)。
当您调用
C.EAT(H)
时,编译器将知道您要使用void Eat(Mammal M)
方法,因为您的h
h < /代码>参考具有类型
哺乳动物
。如果您将对象引用更改为
Horse
或牛
喜欢这样的:输出将是:发生
这种情况,因为编译器将使用最特定的方法,在这种情况下为
基于对象参考类型
。Horse
,void eat(牛C)您可能还对方法覆盖使用运行时多态性。
In your example, method overloading occurs(same method name but different parameter type passed).
When you're calling
c.eat(h)
, the compiler will know that you want to use thevoid eat(Mammal m)
method since yourh
reference has the typeMammal
.If you would change the object reference to
Horse
orCattle
like so:The output will be:
This happens because the compiler will use the most specific method, in this case
void eat(Cattle c)
, based on the object reference typeHorse
.You may also be interested in method overriding which uses runtime polymorphism.