Java继承:如何覆盖父类中的实例变量/字段?

huangapple go评论62阅读模式
英文:

Java Inheritance: How to override instance variables/fields from parent class?

问题

更新:我可以将变量从私有(private)、静态(static)或者常量(final)进行修改。

我有一个父类和一个子类。我想要在父类重用一个方法。通常情况下,只需使用super.methodFromParentClass()就能完成。然而,当我这样做时,我想要重用的方法正在使用父类的实例变量数据,而我不希望出现这种行为。在子类中,我有不同的初始化数据,需要传递到我想要重用的方法中。如果你查看我想要重用的方法(下面只是一个简单的示例,但思想是相同的),我在其中创建了多个对象,这些对象使用了所在类的实例变量。因此,你可以看到,当我调用super.methodIWantToReuse时,它不起作用,因为它会采用父类的数据,并将其传递给对象,即使我真正想要的是传递子类中初始化的数据。我的真实示例还创建了更多的对象和实例变量,因此如果可能的话,我真的希望能够重用这段代码(遵循DRY原则)。

我该如何解决这个问题?是否可以使用getter,即在子类中重写getFirstName()等方法,然后在调用super.methodIWantToReuse()时使用运行时多态性,以便能够获取/使用子类的实例变量数据是唯一的方法??

public class ParentClass {
   private static final String firstName = "Billy Ray";
   private static final String lastName = "Cyrus";
   private static final int age = 58;
   private static final String city = "Hunstville";

   public boolean methodIWantToReuse() {
       Object1 obj1 = new Object(firstName, lastName);
       
       Object2 obj2 = new Object(age, city);
   
       Object3 obj3 = new Object(obj1, obj2);
   
       Object4 obj4 = new Object(obj3);
   
       // 将上面创建的对象作为参数传递,这些对象具有父类的实例变量数据
       return someRandomMethodHere(obj4);
   }
}
public class ChildClass {
    private static final String firstName = "Miley";
    private static final String lastName = "Cyrus";
    private static final int age = 27;
    private static final String city = "Los Angeles";

    @Override
    public boolean methodIWantToReuse() {
       // 不能正确工作,因为最终会使用父类的实例变量数据,但需要使用子类的实例变量数据

       super.methodIWantToReuse();
    }
}
英文:

Update: I can change the variables from being private, static, or final.

I have a parent class and a child class. I want to re-use a method in the parent class. Normally, this is as easy as super.methodFromParentClass() and you're done. However, when I do this, the method I want to re-use is using instance variable data from the Parent class, which is wrong or rather, I do not want this behavior. I have DIFFERENT initialized data in the child class that needs to get passed into the method I want to reuse. If you look at the method I want to re-use (below is just an example to make it simple, but the idea is the same), I am creating multiple objects in there that use the instance variables of the class its called in. So you can see why when I call super.methodIWantToReuse it won't work, because it will take the Parent data and pass it into the objects, even though I really want it to pass the data I initialize in the child class. My real example is also creating way more objects and I have way more instance variables, so I really want to re-use this code (DRY principle) if at all possible.

How can I get around this? Would using getters i.e. getFirstName() and overriding them in the Child class, thus using Runtime Polymorphism when I call super.methodIWantToReuse(), would grab/use the Child class instance variable data be the only way???

public class ParentClass {
   private static final String firstName = "Billy Ray";
   private static final String lastName = "Cyrus";
   private static final int age = 58;
   private static final String city = "Hunstville";

public boolean methodIWantToReuse() {
   Object1 obj1 = new Object(firstName, lastName);
   
   Object2 obj2 = new Object(age,city);

   Object3 obj3 = new Object(obj1, obj2);

   Object4 obj4 = new Object(obj3);

   // Passing in the objects created above as argument, which have the Parent instance variable data
   return someRandomMethodHere(obj4);
}
public class ChildClass {
    private static final String firstName = "Miley";
    private static final String lastName = "Cyrus";
    private static final int age = 27;
    private static final String city = "Los Angeles";

public boolean methodIWantToReuse() {
   // DOESN'T WORK CORRECTLY, because ends up using the instance variable data of PARENT class, but it 
   // needs to use CHILD class instance variable data

   super.methodIWantToReuse();
}

答案1

得分: 3

你不能覆盖类的字段,只能覆盖方法。在你的情况下,你需要使用 getter 方法,并在子类中对它们进行覆盖。

英文:

You can not override fields of a class. Only methods can be overridden. In your case you have to use getters and override them in sub class.

答案2

得分: 3

您的父类实例变量对于该父类是私有的,因此您无法从子类中更新它们。因此,您可以使用参数化方法或为实例变量(或受保护的变量本身)创建受保护的 setter/getter。在您的情况下,这些变量是 final 的,所以实际上您甚至不能更新它们。因此从技术上讲,在父类中不能使用子类变量。

如果您将变量更新为 protected 并且去除 static/final 修饰符(如您在评论中提到的),在调用父类方法之前,请在调用父类方法之前更新变量数据。您可以按照以下方式进行:

方法一: 在调用父类方法之前在父类中更新数据。

父类:

public class ParentClass {
    
    protected String firstName = "Billy Ray";
    protected String lastName = "Cyrus";
    protected int age = 58;
    protected String city = "Hunstville";

    public boolean methodIWantToReuse() {
        // 将上面创建的对象作为参数传递,这些对象具有父类实例变量数据
        Object1 obj1 = new Object(firstName, lastName);
   
        Object2 obj2 = new Object(age, city);

        Object3 obj3 = new Object(obj1, obj2);

        Object4 obj4 = new Object(obj3);
        return someRandomMethodHere(obj4);
    }
}

子类:

public class ChildClass extends ParentClass {
    protected String firstName = "Miley";
    protected String lastName = "Cyrus";
    protected int age = 27;
    protected String city = "Los Angeles";

    public boolean methodIWantToReuse() {
        // 首先在父类中更新数据
        super.firstName = firstName;
        super.lastName = lastName;
        super.age = age;
        super.city = city;
        return super.methodIWantToReuse();
    }
}

方法二: 如果您想使用参数化方法使其无状态,您可以按以下方式进行:

父类:

public class ParentClass {
    
    protected String firstName = "Billy Ray";
    protected String lastName = "Cyrus";
    protected int age = 58;
    protected String city = "Hunstville";

    public boolean methodIWantToReuse() {
        return methodIWantToReuse(this.firstName, this.lastName, this.age, this.city);
    }

    public boolean methodIWantToReuse(String firstName, String lastName, int age, String city) {
        // 将上面创建的对象作为参数传递,这些对象具有父类实例变量数据
        Object1 obj1 = new Object(firstName, lastName);
   
        Object2 obj2 = new Object(age, city);

        Object3 obj3 = new Object(obj1, obj2);

        Object4 obj4 = new Object(obj3);
        return someRandomMethodHere(obj4);
    }
}

子类:

public class ChildClass extends ParentClass {
    protected String firstName = "Miley";
    protected String lastName = "Cyrus";
    protected int age = 27;
    protected String city = "Los Angeles";

    public boolean methodIWantToReuse() {
        // 首先在父类中更新数据
        return super.methodIWantToReuse(this.firstName, this.lastName, this.age, this.city);
    }
}

注意: 将局部变量的名称与类级别的变量相同并不是一个好的做法。但在这里为了方便理解而保持相同。

英文:

Your parent class instance variables are Private to that, so you can't update them from Child class. So rather you use parameterize method or create Protected setter/getter for instance variables (or protected variable itself). In you your case the variables are final so you actually can't even update them. So technically that's not possible to use child class variables in parent class.

If you update your variable to protected and remove static/final modifiers (as you mentioned in comments that you can). Before calling method from parent class, update variable data before calling super method. You can do it as below:

Approach 1 : Updating data in parent class before calling parent class method.

Parent Class:

public class ParentClass {

	protected String firstName = "Billy Ray";
	protected String lastName = "Cyrus";
	protected int age = 58;
	protected String city = "Hunstville";

	public boolean methodIWantToReuse() {
		// Passing in the objects created above as argument, which have the Parent
		// instance variable data
         Object1 obj1 = new Object(firstName, lastName);

         Object2 obj2 = new Object(age,city);

         Object3 obj3 = new Object(obj1, obj2);

         Object4 obj4 = new Object(obj3);
		return someRandomMethodHere(obj4);;
	}
}

Child Class:

public class ChildClass extends ParentClass {
	protected String firstName = "Miley";
	protected String lastName = "Cyrus";
	protected int age = 27;
	protected String city = "Los Angeles";

	public boolean methodIWantToReuse() {
		// Update data in Parent class first

		super.firstName = firstName;
		super.lastName = lastName;
		super.age = age;
		super.city = city;
		return super.methodIWantToReuse();
	}
}

Approach 2 : If you want to use parameterized method to make it stateless, you can do it as below:

Parent Class:

public class ParentClass {

	protected String firstName = "Billy Ray";
	protected String lastName = "Cyrus";
	protected int age = 58;
	protected String city = "Hunstville";

	public boolean methodIWantToReuse() {
		
		return methodIWantToReuse(this.firstName, this.lastName, this.age, this.city);
	}

    public boolean methodIWantToReuse(String firstName, String lastName, int age, String city) {
		// Passing in the objects created above as argument, which have the Parent
		// instance variable data
         Object1 obj1 = new Object(firstName, lastName);

         Object2 obj2 = new Object(age,city);

         Object3 obj3 = new Object(obj1, obj2);

         Object4 obj4 = new Object(obj3);
		return someRandomMethodHere(obj4);;
	}
}

Child Class:

public class ChildClass extends ParentClass {
	protected String firstName = "Miley";
	protected String lastName = "Cyrus";
	protected int age = 27;
	protected String city = "Los Angeles";

	public boolean methodIWantToReuse() {
		// Update data in Parent class first
		return super.methodIWantToReuse(this.firstName, this.lastName, this.age, this.city);
	}
}

NOTE: It's not good practice to keep local variables name same as the class level variables. But kept it here same for just understanding.

答案3

得分: 0

如果你确实指的是实例变量,而不是你示例中的静态变量(或类变量),你可以通过更改访问修饰符并移除final关键字来使它们对子类可访问。

然而,如果你实际指的是静态变量,你不能在每个子类中重新分配它们,因为它们将共享由ParentClass定义的相同静态变量,这意味着调用ParentClass#methodIWantToReuse只会得到最后加载的类的结果。

最好的方法是通过使用面向对象编程(OOP)来实例化具有所需参数的新个体对象,并使用它们。

我的意思是,不要这样做:

public class Example {
  public static class ParentClass {
    protected String name;
    protected int age;
    
    public ParentClass() {
      name = "The parent";
      age = 35;
    }
    
    public String methodIWantToReuse() {
      return name + " is " + age + " years old.";
    }
  }
  
  public static class AChildClass extends ParentClass {
    public AChildClass() {
      name = "Alice";
      age = 13;
    }
  }
  
  public static class AnotherChildClass extends ParentClass {
    public AnotherChildClass() {
      name = "Bob";
      age = 21;
    }
  }
  
  public static void main(String[] args) {
    // Prints "The parent is 35 years old."
    System.out.println(new ParentClass().methodIWantToReuse());
    // Prints "Alice is 13 years old."
    System.out.println(new AChildClass().methodIWantToReuse());
    // Prints "Bob is 21 years old."
    System.out.println(new AnotherChildClass().methodIWantToReuse());
  }
}

这样做:

public class Example {
  public static class ParentClass {
    protected String name;
    protected int age;
    
    // Variables instantiated here to not cause confusion
    public ParentClass() {
      name = "The parent";
      age = 35;
    }
    
    public String methodIWantToReuse() {
      return name + " is " + age + " years old.";
    }
  }
  
  public static class ChildClass extends ParentClass {
    public ChildClass(String name, int age) {
      this.name = name;
      this.age = age;
    }
  }
  
  public static void main(String[] args) {
    // Prints "The parent is 35 years old."
    System.out.println(new ParentClass().methodIWantToReuse());
    // Prints "Alice is 13 years old."
    System.out.println(new ChildClass("Alice", 13).methodIWantToReuse());
    // Prints "Bob is 21 years old."
    System.out.println(new ChildClass("Bob", 21).methodIWantToReuse());
  }
}

这也符合DRY原则,因为你想要尽可能高效地重用代码,而不是一遍又一遍地技术性地编写相同的代码。

正如你所看到的,我没有必要覆盖ParentClass#methodIWantToReuse或调用ChildClass的超类实现。

英文:

In case you really mean instance variables instead of your static variables (or class variables) as shown in your example, you could make them accessible for your subclass by changing the access modifier and removing the final keyword.

If, however, you actually mean static variables, you cannot reassign them in each subclass as they would all share the same static variables defined by the ParentClass, meaning the last loaded class would be the only result you get by calling your ParentClass#methodIWantToReuse.

Best would be to use OOP to your advantage by instantiating new individual objects with the required arguments, and using them.

By this I mean instead of doing this:

public class Example {
  public static class ParentClass {
    protected String name;
    protected int age;
    
    public ParentClass() {
      name = "The parent";
      age = 35;
    }
    
    public String methodIWantToReuse() {
      return name + " is " + age + " years old.";
    }
  }
  
  public static class AChildClass extends ParentClass {
    public AChildClass() {
      name = "Alice";
      age = 13;
    }
  }
  
  public static class AnotherChildClass extends ParentClass {
    public AnotherChildClass() {
      name = "Bob";
      age = 21;
    }
  }
  
  public static void main(String[] args) {
    // Prints "The parent is 35 years old."
    System.out.println(new ParentClass().methodIWantToReuse());
    // Prints "Alice is 13 years old."
    System.out.println(new AChildClass().methodIWantToReuse());
    // Prints "Bob is 21 years old."
    System.out.println(new AnotherChildClass().methodIWantToReuse());
  }
}

Do this:

public class Example {
  public static class ParentClass {
    protected String name;
    protected int age;
    
    // Variables instantiated here to not cause confusion
    public ParentClass() {
      name = "The parent";
      age = 35;
    }
    
    public String methodIWantToReuse() {
      return name + " is " + age + " years old.";
    }
  }
  
  public static class ChildClass extends ParentClass {
    public ChildClass(String name, int age) {
      this.name = name;
      this.age = age;
    }
  }
  
  public static void main(String[] args) {
    // Prints "The parent is 35 years old."
    System.out.println(new ParentClass().methodIWantToReuse());
    // Prints "Alice is 13 years old."
    System.out.println(new ChildClass("Alice", 13).methodIWantToReuse());
    // Prints "Bob is 21 years old."
    System.out.println(new ChildClass("Bob", 21).methodIWantToReuse());
  }
}

This should also be along the lines of the DRY principle, as you want to reuse your code as efficient as possible instead of coding technically the same over and over again.

As you can see, there was no need for me to override ParentClass#methodIWantToReuse or call the ChildClass' super's implementation.

huangapple
  • 本文由 发表于 2020年8月17日 12:16:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/63444498.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定