在java中,我们可以通过传递超类方法中使用的参数的子类来覆盖方法吗?

时间:2015-08-04 23:06:08

标签: java overloading override method-overriding

根据规则,在覆盖子类中的方法时,参数不能更改,并且必须与超类中的参数相同。 如果我们在重写方法时传递参数的子类怎么办? 是否会被称为超载或覆盖?

根据我的查询,我在下面写了一些代码 我期待输出为“狗吃肉食”但令我惊讶的是输出是“动物吃肉食” 如果有人能够解释当分配的对象是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);
        }
    }

7 个答案:

答案 0 :(得分:12)

eat中的方法Dog未覆盖eat中的方法Animal。这是因为参数不同(一个需要Flesh,另一个需要Food)。

eat方法是重载。

重载之间的选择发生在编译时,而不是运行时。它是基于调用该方法的对象的实际类,而是基于编译时类型(如何声明变量)。

animal具有编译时类型Animal。我们知道这一点,因为变量animal的声明是Animal animal = ...。事实上它实际上是Dog是无关紧要的 - 它是eatAnimal的版本必须被调用。

另一方面,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),因此您正在重载而不是覆盖。