英文:
Java: polymorphically call super implementation
问题
假设我有这样的代码:
public class A {
public String foo() { return "A"; }
}
public class B extends A {
public String foo() { return "B"; }
public String superFoo() { return super.foo(); }
}
public class C extends B {
public String foo() { return "C"; }
}
在这里,new C().superFoo()
返回 "A"
。
是否有一种方式可以通过多态调用 B.foo()
(从而返回 "B"
)而不需要在 C
中重写 superFoo()
?
我尝试使用反射(像这样重新定义 B.superFoo()
:return getClass().getSuperclass().getDeclaredMethod("foo").invoke(this)
),希望通过 getDeclaredMethod
我可以引用超类中确切的方法实现,但在这种情况下我得到了 "C"
(因此,多态被应用)。
我正在寻找一种不需要我在添加新的子类到继承层次结构时重新声明 superFoo()
的解决方案。
英文:
Suppose I have this:
public class A {
public String foo() { return "A"; }
}
public class B extends A {
public String foo() { return "B"; }
public String superFoo() { return super.foo(); }
}
public class C extends B {
public String foo() { return "C"; }
}
Here, new C().superFoo()
returns "A"
.
Is there a way I can polymorphically make new C().superFoo()
invoke B.foo()
(and hence return "B"
) without the need to override superFoo()
in C
?
I tried with reflection (redefining B.superFoo()
like this: return getClass().getSuperclass().getDeclaredMethod("foo").invoke(this)
), hoping that with getDeclaredMethod
I could reference the exact method implementation in superclass, but I get "C"
in that case (hence, polymorphism is applied).
I was searching for a solution that doesn't require me to redeclare superFoo()
whenever I add a new subclass to the hierarchy.
答案1
得分: 2
TL;DR
从问题和评论中看,似乎要逐步构建行为。从不同的角度来看,在这种情况下,我更喜欢使用组合而不是继承。
您可以使用装饰器模式将实例组合在一起;这将为您提供对父类的 foo()
实现的引用。另一个好处是您可以在运行时扩展/更改行为,这在静态继承设计中是不可能的。
关于装饰器模式
装饰器模式可用于静态或动态地附加附加职责到对象上。
Component
- 用于可以动态添加职责的对象的接口。ConcreteComponent
- 定义了可以添加额外职责的对象。Decorator
- 维护对Component
对象的引用,并定义符合Component
接口的接口。Concrete Decorators
- 具体装饰器通过添加状态或行为来扩展组件的功能。
示例代码
让我们以比萨烘焙过程为例。
Component
接口 - 定义了比萨必须烘烤的合同。
public interface Pizza {
void bake();
}
ConcreteComponent
类 - 这是您的接口实现,可以独立存在。它不应扩展 Decorator
,并且在将对象组合在一起时,它出现在最内部位置(请参见最后的客户端代码)。
public class VeggiePizza implements Pizza {
@Override
public void bake() {
System.out.println("I'm a Veggie Pizza in the making :)");
}
}
Decorator
- 指定扩展 ConcreteComponent
功能的合同。
public abstract class Topping implements Pizza {
private Pizza pizza;
public Topping(Pizza pizza) {
this.pizza = pizza;
}
@Override
public void bake() {
pizza.bake();
}
}
Concrete Decorator
- 这些实现通过将它们的构造函数嵌套在一起(组合的一种方式)来扩展 ConcreteComponent
的功能。具体的装饰器可以在组合时出现在任何位置,除了最内部位置(请参见下面的客户端代码)。
在这里,我们定义了两种配料 - 蘑菇和辣椒。
public class Mushroom extends Topping {
public Mushroom(Pizza pizza) {
super(pizza);
}
@Override
public void bake() {
addMushroom();
super.bake();
}
private void addMushroom() {
System.out.println("Adding mushrooms...");
}
}
public class Jalapeno extends Topping {
public Jalapeno(Pizza pizza) {
super(pizza);
}
@Override
public void bake() {
addJalapenos();
super.bake();
}
private void addJalapenos() {
System.out.println("Adding jalapenos...");
}
}
客户端代码 - 如何将 ConcreteDecorator
和 ConcreteComponent
组合在一起?
public void bakePizza() {
Pizza pizza = new Mushroom(new Jalapeno(new VeggiePizza()));
pizza.bake();
}
请注意,我们通过在 VeggiePizza
周围包装对象并添加来自 Mushroom
和 Jalapeno
的附加行为来构建 VeggiePizza
。在这里,ConcreteComponent
是最内部的 VeggiePizza
,而我们的 ConcreteDecorator
是 Jalapeno
和 Mushroom
。
注意: 构造函数组合只是组合的一种方式。您可以通过设置器组合对象,或使用依赖注入框架。
输出
Adding mushrooms...
Adding jalapenos...
I'm a Veggie Pizza in the making :)
英文:
TL;DR
Going through the question and comments, it seems like the ask here is to incrementally build up on a behavior. Taking a different perspective, I would prefer Composition over Inheritance in this scenario.
You can use Decorator pattern and compose the instances together; which in turn gives you a reference to the parent's foo()
implementation. One of the other benefits is that you can extend/change the behavior at runtime, which is not possible with a static inheritance design.
About Decorator Pattern
Decorator pattern can be used to attach additional responsibilities to an object either statically or dynamically.
Component
- Interface for objects that can have responsibilities added to them dynamically.ConcreteComponent
- Defines an object to which additional responsibilities can be added.Decorator
- Maintains a reference to a Component object and defines an interface that conforms to Component's interface.Concrete Decorators
- Concrete Decorators extend the functionality of the component by adding state or adding behavior.
Sample Code
Let's take a Pizza baking process as an example.
Component
interface - Defines the contract that a Pizza must be baked.
public interface Pizza {
void bake();
}
ConcreteComponent
class - This is your implementation of the interface which can stand alone by itself. It should not extend the Decorator
and it appears at the innermost position when the objects are composed together (see client code at the end)
public class VeggiePizza implements Pizza {
@Override
public void bake() {
System.out.println("I'm a Veggie Pizza in the making :)");
}
}
Decorator
- Specifies a contract for extending the functionality of the ConcreteComponent
.
public abstract class Topping implements Pizza {
private Pizza pizza;
public Topping(Pizza pizza) {
this.pizza = pizza;
}
@Override
public void bake() {
pizza.bake();
}
}
Concrete Decorator
- These implementations add to the functionality of the ConcreteComponent
by nesting their constructors together (one of the ways to compose!). The concrete decorator can appear anywhere while composing, except for the innermost position (see client code below).
Here we are defining two toppings - Mushroom and Jalapeno.
public class Mushroom extends Topping {
public Mushroom(Pizza pizza) {
super(pizza);
}
@Override
public void bake() {
addMushroom();
super.bake();
}
private void addMushroom() {
System.out.println("Adding mushrooms...");
}
}
public class Jalapeno extends Topping {
public Jalapeno(Pizza pizza) {
super(pizza);
}
@Override
public void bake() {
addJalapenos();
super.bake();
}
private void addJalapenos() {
System.out.println("Adding jalapenos...");
}
}
Client code - How do you compose the ConcreteDecorator
and ConcreteComponent
together?
public void bakePizza() {
Pizza pizza = new Mushroom(new Jalapeno(new VeggiePizza()));
pizza.bake();
}
Notice that we build upon the VeggiePizza
by wrapping the objects around with additional behavior from Mushroom
and Jalapeno
. Here, the ConcreteComponent
is the innermost VeggiePizza
, while our ConcreteDecorator
s are Jalapeno
and Mushroom
.
Note: Constructor composition is only one of the ways to compose. You can compose object together via setters or use a Dependency Injection framework.
Output
Adding mushrooms...
Adding jalapenos...
I'm a Veggie Pizza in the making :)
答案2
得分: 0
以下将返回 B
,尽管为了简洁起见,我省略了各种安全功能,并使用了 commons-lang
,因为您不希望自己处理这些东西!至少,此代码假定每个类都定义了 foo()
,并且您从未直接调用过 a.superFoo()!
public String superFoo() {
return superXXX("foo");
}
private <T> T superXXX(String name, Object... args) {
Method overriddenMethod = MethodUtils.getAccessibleMethod(getClass(), name);
Iterator<Method> methods = MethodUtils.getOverrideHierarchy(overriddenMethod, EXCLUDE).iterator();
methods.next(); // this is C
Method parentMethod = methods.next(); // This is B;
try {
return (T) parentMethod.invoke(this, args);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
FYI. 可能还有一种 AspectJ/Javassist/Bytebuddy 风格的解决方案,可以重新实现 A
的所有子类上的 superFoo
方法为 super.foo()
。
英文:
Following will return B
though I've omitted various safety features for the sake of brevity and used commons-lang
because you don't want to have to do this stuff yourself! At a minimum, this code assumes every class defines foo()
and the you never directly call a.superFoo()!
public String superFoo() {
return superXXX("foo");
}
private <T> T superXXX(String name, Object... args) {
Method overriddenMethod = MethodUtils.getAccessibleMethod(getClass(), name);
Iterator<Method> methods = MethodUtils.getOverrideHierarchy(overriddenMethod, EXCLUDE).iterator();
methods.next(); // this is C
Method parentMethod = methods.next(); // This is B;
try {
return (T)parentMethod.invoke(this, args);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
FYI. There may well be an AspectJ/Javassist/Bytebuddy style solution possible as well whereby you can reimplement the superFoo
method on all children of A
to be super.foo()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论