根据规则,在覆盖子类中的方法时,参数不能更改,并且必须与超类中的参数相同。 如果我们在重写方法时传递参数的子类怎么办? 是否会被称为超载或覆盖?
根据我的查询,我在下面写了一些代码 我期待输出为“狗吃肉食”但令我惊讶的是输出是“动物吃肉食” 如果有人能够解释当分配的对象是Dog类型时会如何调用Animal方法,那将会很感激。
class Food {
public String toString(){
return "Normal Food";
}
}
class Flesh extends Food {
public String toString(){
return "Flesh Food";
}
}
class Animal {
public void eat(Food food){
System.out.println("Animal eats "+ food);
}
}
class Dog extends Animal{
public void eat(Flesh flesh){
System.out.println("Dog eats "+ flesh);
}
}
public class MyUtil {
public static void main(String[] args) {
Animal animal = new Dog();
Flesh flesh = new Flesh();
animal.eat(flesh);
}
}
答案 0 :(得分:12)
eat
中的方法Dog
未覆盖eat
中的方法Animal
。这是因为参数不同(一个需要Flesh
,另一个需要Food
)。
eat
方法是重载。
重载之间的选择发生在编译时,而不是运行时。它是不基于调用该方法的对象的实际类,而是基于编译时类型(如何声明变量)。
animal
具有编译时类型Animal
。我们知道这一点,因为变量animal
的声明是Animal animal = ...
。事实上它实际上是Dog
是无关紧要的 - 它是eat
中Animal
的版本必须被调用。
另一方面,toString
中的Flesh
方法会覆盖toString
中的Food
方法。
当一个方法覆盖另一个方法时,调用该方法的对象的实际类确定运行哪个版本。
在eat
的{{1}}方法中,即使参数具有编译时类型Animal
,如果您将Food
的实例传递给它,它也是Flesh
中将toString
执行的Flesh
方法。
因此您收到消息"Animal eats Flesh Food"
。
答案 1 :(得分:3)
不,我们不能在这种情况下你可以通过在Dog类的eat方法上面添加@Override来检查它,所以编译错误会出现如下:
@Override
public void eat(Flesh flesh){
System.out.println("Dog eats "+ flesh);
}
确保在覆盖中,参数应该是类型和顺序中的父类。
答案 2 :(得分:3)
您可以使用@override
注释通知编译器您正在尝试覆盖超类中的方法。
e.g。
class Dog extends Animal{
@Override
public void eat(Flesh flesh){
System.out.println("Dog eats "+ flesh);
}
}
在您的代码中,现在编译器将生成错误,因为此方法不会覆盖eat
,您需要具有相同的参数类型。
但是,将Flesh
参数更改为Food
类型可以解决问题:
class Dog extends Animal{
@Override
public void eat(Food food){
System.out.println("Dog eats "+ food);
}
}
现在您可以执行以下操作:
public class MyUtil {
public static void main(String[] args) {
Animal animal = new Dog();
Food food = new Flesh();
animal.eat(food);
}
}
答案 3 :(得分:1)
当你这样做时:
Animal animal = new Dog();
它正在向上发展。
并且您不会覆盖方法
public void eat(Flesh flesh){
System.out.println("Dog eats "+ flesh);
}
所以它调用了eat(Food food)
Animal
答案 4 :(得分:0)
在java中,我们可以通过传递超类方法中使用的参数的子类来覆盖方法吗?
NO。
示例说明:
假设我们可以通过传递超类方法中使用的参数的子类来覆盖方法。
class A {
public void print(Oject obj)
{
System.out.println(obj.toString());
}
}
class B extends A {
@Override
public void print(String str)
{
System.out.println(str.toString());
}
}
class C extends A {
@Override
public void print(Integer in) {
System.out.println(in.toString());
}
}
class MainTest {
public static void main(String args[])
{
Integer in = new Integer(3);
A a = new B(); // developer by mistake types new B() instead of new C()
a.print(in); // compiler will allow this because class A has print method which can take any subclass of Object type(in this case any class because Object is superclass of all classes in java).
}
}
但想想运行期间会发生什么?
在运行时,将在引用指向的实际对象上调用方法。在我们的例子中它指向B类。 所以JVM会调查B类的方法执行,这对JVM来说是一个很大的冲击,因为B类没有覆盖 带有签名public void print(Integer in)的方法,它将在运行时失败。
这样做的结果是应用程序将在运行时而不是编译时崩溃。
覆盖Java中的规则:
=============================
在你的例子中会发生什么:
在编译期间:编译器确保可以在引用类型(即Aniaml类)上调用eat方法。 由于Animal类有一个方法public void eat(食物食品),它可以采取任何食物类型和亚类 食物类型作为输入参数(多态),编译传递。
通常,编译器保证特定方法可以为特定引用类型调用。
在运行时:实际调用的方法是,该对象类型的最具体版本的方法(Dog类)。 JVM无法找到重写方法,即声明了相同的方法名称和相同的参数类型 在子类(Dog类)中,它检查其超类(Animal类)并找到该方法并执行。
public void eat(食物食品) 和 public void eat(肉体) 从重写方法的角度来看是不一样的。
通常,当您在对象引用上调用方法时,您需要为该方法调用最具体的方法版本 运行时期间的对象类型但如果它找不到它,那么它将会升级。最坏的情况是超级类肯定会有 方法实现,否则编译不会通过。
答案 5 :(得分:0)
To be able to "dynamically load the subclass at runtime", use of generics makes it work perfectly.
public class HelloWorld{
public static void main(String []args){
Animal animal = new Dog();
Food flesh = new Flesh();
animal.eat(flesh);
}
}
class Food {
public String toString(){
return "Normal Food";
}
}
class Flesh extends Food {
public String toString(){
return "Flesh Food";
}
}
class Animal<T extends Food> {
public void eat(T food){
System.out.println("Animal eats "+ food);
}
}
class Dog extends Animal<Flesh>{
public void eat(Flesh flesh){
System.out.println("Dog eats "+ flesh);
}
}
答案 6 :(得分:0)
事实上你不能@Override一个具有不同签名的方法,看这个例子:
public class Clase1 {
public void metodo1 (ClasePadre atr1) {
System.out.println("Hola soy Clase1");
}
}
public class Clase2 extends Clase1 {
public void metodo1 (ClaseHija atr1) {
System.out.println("Hola soy Clase2");
}
}
public class ClasePadre {
public void metodo1 () {
System.out.println("Hola soy ClasePadre");
}
}
public class ClaseHija extends ClasePadre {
public void metodo1 () {
System.out.println("Hola soy ClaseHija");
}
}
当你做一个main方法并实例化这个Clase2 clase2 = new Clase2();您将看到两种不同的方法metodo1(ClaseHija atr1) 和metodo2(ClasePadre atr1),因此您正在重载而不是覆盖。