能否在 java 中创建一个可扩展的类层次结构,其方法是流畅的并且可以按任何顺序调用? (是!请参阅下面的答案),即使对于现有的类,当您无法访问源代码时,只要方法是流畅的!
我正在改造现有的层次结构,并希望使用工厂或至少使用通用构造函数和(最终)不可变的构建器模式(JB P.14)。设置字段的方法返回void
- 他们最好返回一个通用的 T
- 这样我们将获得进行方法链接的能力(他们都调用 super
现在)。
目标:
1. 避免必须在每个类中创建静态 getFactory() 方法。
2. 简单的方法签名。
3. 创建一个通用的工厂方法,但会在编译时捕获问题。
4. 出错时获取编译时错误而不是运行时错误。
根据要求,非通用代码非常简单,但不起作用。
public class A {
private String a = null;
protected A setA(String a){
this.a = a;
return this;//<== DESIRE THIS TO BE CHAINABLE
}
protected static A factory(){
return new A();
}
}
.
public class B extends A {
private String b = null;
protected Foo setB(String b){
this.b = b;
return this;//<== DESIRE THIS TO BE CHAINABLE
}
protected static B factory(){
return new B();
}
}
现在调用者可以尝试调用 B.factory().setA("a").setB("b")//不会编译
但这无法编译,因为 setA()
返回的是 A
,而不是 B
。您可以通过覆盖 B
中的 setA()
,调用 setB()
并返回 B
来使其工作A
的。关键是要避免委托(delegate)这些方法中的每一个。我只是想要一组可以按任何顺序调用的可扩展的可链接类方法。 B.getFactory().B("b").A("a")
效果明显。
最佳答案
答案(令我惊讶和满意)是肯定的。我回答了这个question我: 如果方法调用返回相关类的实例(请参阅下面的可链接),您可以通过一些工作来完成此操作。如果您可以编辑顶级源代码,我还发现了一种更简单的方法:
在顶级类 (A) 中:
protected final <T> T a(T type) {
return type
}
假设 C 扩展 B,B 扩展 A。
调用:
C c = new C();
//Any order is fine and you have compile time safety and IDE assistance.
c.setA("a").a(c).setB("b").a(c).setC("c");
示例 1 和 3 是使现有类层次结构流畅并允许以任何顺序调用方法的方法,只要现有类流畅(但您无权访问或无法更改源). WAY2 是一个您可以访问源代码并希望调用尽可能简单的示例。
完整的 SSCCE:
import static java.lang.System.out;
public class AATester {
public static void main(String[] args){
//Test 1:
for(int x: new int[]{ 0, 1, 2 } ){
A w = getA(x);
//I agree this is a nasty way to do it... but you CAN do it.
Chain.a(w.setA("a1")).a(w instanceof C ? ((C) w).setC("c1") : null );
out.println(w);
}
//Test for WAY 2: Hope this wins Paul Bellora's approval
//for conciseness, ease of use and syntactic sugar.
C c = new C();
//Invoke methods in any order with compile time type safety!
c.setA("a2").a(c).setB("b2").a(c).set("C2");
out.println(w);
//Example 3, which is Example 1, but where the top level class IS known to be a "C"
//but you don't have access to the source and can't add the "a" method to the
//top level class. The method invocations don't have to be as nasty as Example 1.
c = new C();
Chain.a(c.setA("a3")).a(c.setB("b3")).a(c.setC("c3"));//Not much larger than Example 2.
out.println(w);
}
public static getA(int a){//A factory method.
A retval;//I don't like multiple returns.
switch(a){
case 0: retval = new A(); break;
case 1: retval = new B(); break;
default: retval = new C(); break;
}
return retval;
}
}
测试类A
public class A {
private String a;
protected String getA() { return a; }
//WAY 2 - where you have access to the top level source class.
protected final <T> T a(T type) { return type; }//This is awesome!
protected A setA(String a) { this.a=a; return this; }//Fluent method
@Override
public String toString() {
return "A[getA()=" + getA() + "]";
}
}
测试类B
public class B extends A {
private String b;
protected String getB() { return b; }
protected B setB(String b) { this.b=b; return this; }//Fluent method
@Override
public String toString() {
return "B[getA()=" + getA() + ", getB()=" + getB() + "]\n "
+ super.toString();
}
}
测试类C
public class C extends B {
private String c;
protected String getC() { return c; }
protected C setC(String c) { this.c=c; return this; }//Fluent method
@Override
public String toString() {
return "C [getA()=" + getA() + ", getB()=" + getB() + ", getC()="
+ getC() + "]\n " + super.toString();
}
}
链类
/**
* Allows chaining with any class, even one you didn't write and don't have
* access to the source code for, so long as that class is fluent.
* @author Gregory G. Bishop ggb667@gmail.com (C) 11/5/2013 all rights reserved.
*/
public final class Chain {
public static <K> _<K> a(K value) {//Note that this is static
return new _<K>(value);//So the IDE names aren't nasty
}
}
Chain 的辅助类。
/**
* An instance method cannot override the static method from Chain,
* which is why this class exists (i.e. to suppress IDE warnings,
* and provide fluent usage).
*
* @author Gregory G. Bishop ggb667@gmail.com (C) 11/5/2013 all rights reserved.
*/
final class _<T> {
public T a;//So we may get a return value from the final link in the chain.
protected _(T t) { this.a = t }//Required by Chain above
public <K> _<K> a(K value) {
return new _<K>(value);
}
}
输出:
A [get(A)=a]
B [get(A)=a, getB()=null]
A [getA()=a]
C [getA()=a, getB()=null, getC()=c)]
B [get(A)=a, getB()=null]
A [get(A)=a]
QED。 :)
我从未见过有人这样做过;我认为这可能是一种具有潜在值(value)的新技术。
附言关于“elvis like usage”,它是 1 或 2 行 vs 8 或更多。
Book b = null; Publisher p = null; List books = null; String id = "Elric of Melnibone"; books = Chain.a(b = findBook(id)).a(b != null ? p = b.getPublisher() : null) .a(p != null ? p.getPublishedBooks(): null).a; out.println(books==null ? null : Arrays.toString(books.toArray()));
对比:
Book b = null; Publisher p = null; List books = null; String id = "Elric of Melnibone"; b = findBook(id); Array[] books = null; if( b != null ) { p = b.getPublisher(); if(p != null) { books = p.getPublishedBooks(); } } out.println(books==null ? null : Arrays.toString(books.toArray()));
没有 NPE,如果链条完成,您将获得“Elric of Melnibone”出版商出版的所有书籍(即“Ace”出版商出版的所有书籍),否则您将获得空值。
关于java - 在 java 中,是否可以创建一个流畅的可扩展类层次结构,其中的方法可以按任何顺序调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18027517/