Java 如何强制类的任何子类始终调用父类';它们覆盖的是什么实现方法?
假设我有一个类,它实现了一个方法(addThings())。它是一个子类树的基础:Java 如何强制类的任何子类始终调用父类';它们覆盖的是什么实现方法?,java,oop,inheritance,design-patterns,overriding,Java,Oop,Inheritance,Design Patterns,Overriding,假设我有一个类,它实现了一个方法(addThings())。它是一个子类树的基础: ParentClass { protected void addThings() { map.add(thing1); map.add(thing2); } } 现在,假设我们实现了一个child类(它也有thing3),thing3还需要添加到thing1和thing2之上 显而易见的Java解决方案似乎是让子类的方法实现调用super的方法: ChildCla
ParentClass {
protected void addThings() {
map.add(thing1);
map.add(thing2);
}
}
现在,假设我们实现了一个child类(它也有thing3),thing3还需要添加到thing1和thing2之上
显而易见的Java解决方案似乎是让子类的方法实现调用super的方法:
ChildClass extends ParentClass {
protected void addThings() {
super.addThings();
map.add(thing3);
}
}
问题是,无论是谁实现了子类,都很可能忘记做那件事,并且有一个bug:
ChildClassBad extends ParentClass {
protected void addThings() {
// BUG!!! Forgot to call super.addThings(); !!!
map.add(thing3);
}
}
Java中有没有一种方法可以强制任何扩展的子类(和孙子类)在重写父类方法时总是调用父类的方法?(类似于使方法抽象总是迫使他们实现它)
- 请注意,我需要解决方案能够沿着继承树向下传播 换句话说,如果有人实现了一个需要添加内容4的类,那么相对于ChildClass,他们可能会遇到相同的bug 这意味着,在父类中有单独的“addParentThings()”然后同时调用addParentThings()和子可重写的空addThings()的简单修复(当您只有1级继承时适用)是不够的(因为孙子必须重写非空的addThings)
class ParentClass {
public final void doStuff() {
// Do stuff
onPostDoStuff();
}
protected void onPostDoStuff() {
// Override this!
}
}
然后在儿童班:
class ChildClass extends ParentClass {
@Override
protected void onPostDoStuff() {
// Do extra stuff
}
}
您甚至可以将
onPostDoStuff()
方法抽象化,因此孩子们必须重写它。我想不出任何东西可以满足您在未来子类中仍然可以强制执行的条件
Android sdk在其许多核心生命周期方法中都有“super未被调用”的例外情况,但它严格来说是单级继承。如果您愿意为每个类使您的
doStuff
-方法保持静态,这扩展了您的父类
,并为您的父类
提供了一个最终公共void doAllStuff()
-方法,您可以通过反射解决问题:
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class Main
{
public static void main(String[] args) throws InterruptedException
{
A a = new C();
a.doAllStuff();
}
}
class A
{
protected List<String> list = new ArrayList<String>();
@SuppressWarnings("unused")
private static void doStuff(A a)
{
a.list.add("I am A");
}
final public void doAllStuff()
{
List<Class<?>> list = new ArrayList<Class<?>>();
Class<?> clazz = this.getClass();
while (A.class.getSuperclass() != clazz)
{
list.add(clazz);
clazz = clazz.getSuperclass();
}
System.out.println(list);
for (Class<?> myClass : list)
{
try
{
Method method = myClass.getDeclaredMethod("doStuff"
, myClass);
// Method is private? Make it accessible anyway.
method.setAccessible(true);
method.invoke(this, this);
}
catch (NoSuchMethodException e)
{
// Method not found, continue with next class.
continue;
}
catch (Exception e)
{
e.printStackTrace();
}
}
System.out.println(this.list);
}
}
class B extends A
{
@SuppressWarnings("unused")
private static void doStuff(B b)
{
b.list.add("I am B");
}
}
class C extends B {}
import java.lang.reflect.Method;
导入java.util.ArrayList;
导入java.util.List;
公共班机
{
公共静态void main(字符串[]args)引发InterruptedException
{
A=新的C();
a、 doAllStuff();
}
}
甲级
{
受保护列表=新的ArrayList();
@抑制警告(“未使用”)
专用静态空隙度凝灰岩(A)
{
a、 列表。添加(“我是一名”);
}
最终公开作废doAllStuff()
{
列表>();
Class clazz=this.getClass();
while(A.class.getSuperclass()!=clazz)
{
列表。添加(clazz);
clazz=clazz.getSuperclass();
}
系统输出打印项次(列表);
对于(类myClass:list)
{
尝试
{
Method=myClass.getDeclaredMethod(“doStuff”
,myClass);
//方法是私有的?无论如何都要使其可访问。
方法setAccessible(true);
调用(这个,这个);
}
捕获(无此方法例外)
{
//方法未找到,请继续下一个类。
继续;
}
捕获(例外e)
{
e、 printStackTrace();
}
}
System.out.println(这个列表);
}
}
B类扩展了A类
{
@抑制警告(“未使用”)
专用静态空隙度凝灰岩(B)
{
b、 列表。添加(“我是b”);
}
}
类C扩展了B{}
如果只需要调用属性,可以使用
getDeclaredField
,在这种情况下,字段可能不是static
。以下方法强制调用超类的setup方法。缺点,或者子类中可能存在的bug,是实现者可能忘记提供合适的构造函数来提供setup方法的扩展。这意味着这个孩子不能由孙辈抚养
它也很难看,因为子类不能将子类特定的设置放在它们的扩展中;他们必须接受Parent
作为参数
总的来说,这里的尴尬困难表明,在静态分析器中执行更好,如果有的话,而不是javac
public class Parent
{
private final Consumer<Parent> setup;
protected final Collection<Object> x = new ArrayList<>();
public Parent()
{
setup = Parent::setupImpl;
}
protected Parent(Consumer<Parent> extension)
{
setup = ((Consumer<Parent>) Parent::setupImpl).andThen(extension);
}
public final void setup()
{
setup.accept(this);
}
private static void setupImpl(Parent it)
{
it.x.add("thing1");
it.x.add("thing2");
}
}
public class Child
extends Parent
{
public Child()
{
super(Child::setupImpl);
}
protected Child(Consumer<Parent> extension)
{
super(((Consumer<Parent>) Child::setupImpl).andThen(extension));
}
private static void setupImpl(Parent it)
{
it.x.add("thing3");
}
}
公共类父类
{
私人最终消费者设置;
受保护的最终集合x=新的ArrayList();
公共家长()
{
setup=Parent::setupImpl;
}
受保护的父级(使用者扩展)
{
setup=((使用者)父::setupImpl).然后(扩展名);
}
公共最终无效设置()
{
设置。接受(此);
}
专用静态void setupImpl(其父级)
{
it.x.添加(“内容1”);
it.x.添加(“内容2”);
}
}
公营儿童
扩展父级
{
公共儿童()
{
super(Child::setupImpl);
}
受保护的孩子(消费者扩展)
{
super(((消费者)子级::setupImpl.and(扩展));
}
专用静态void setupImpl(其父级)
{
it.x.添加(“内容3”);
}
}
这是一种稍微不同的解决问题的方法,正如对所选答案的一条评论中所说的,您可以使用decorator模式(它与传统的decorator略有不同,适合于此问题),在我看来,它是一种更干净的解决方案。我添加了两个类,分别添加了thing3和thing4来显示用法
public interface ThingAdder {
void addThings();
}
public abstract class AbstractAdder implements ThingAdder {
protected List<String> map = new ArrayList<>(); // or your map impl
}
public class DefaultAdderDecorator implements ThingAdder {
AbstractAdder decoratedThingAdder;
public DefaultAdderDecorator(AbstractAdder decoratedThingAdder) {
this.decoratedThingAdder = decoratedThingAdder;
}
@Override
public void addThings() {
decoratedThingAdder.map.add("thing 1");
decoratedThingAdder.map.add("thing 2");
decoratedThingAdder.addThings();
}
}
public class Thing3Adder extends AbstractAdder {
@Override
public void addThings() {
map.add("thing 3");
}
}
public class Thing4Adder extends AbstractAdder {
@Override
public void addThings() {
map.add("thing 4");
}
}
public class AdderApp {
public static void main(String args[]) {
Thing3Adder thing3Adder = new Thing3Adder();
Thing4Adder thing4Adder = new Thing4Adder();
ThingAdder decoratedAdder = new DefaultAdderDecorator(thing3Adder);
decoratedAdder.addThings();
System.out.println("Decorated Thing3Adder map:"+thing3Adder.map);
decoratedAdder = new DefaultAdderDecorator(thing4Adder);
decoratedAdder.addThings();
System.out.println("Decorated Thing4Adder map:"+thing4Adder.map);
}
}
decorator模式背后的思想是扩展现有功能,在本例中,我们通过使用默认装饰来扩展addThings方法,该装饰在调用装饰对象自己的addThings方法之前添加了thing1和thing2,然后,每当需要有一个需要先插入默认值的新加法器时,开发人员只需创建一个扩展的新ThingXAdder
Decorated Thing3Adder map:[thing 1, thing 2, thing 3]
Decorated Thing4Adder map:[thing 1, thing 2, thing 4]