代码之家  ›  专栏  ›  技术社区  ›  marciel.deg

作为接口返回的方法

  •  1
  • marciel.deg  · 技术社区  · 6 年前

    考虑这个例子:

    我有3个接口:a,b,c,方法a(),b(),c();扩展一个基接口intf;

    我有一个带有选项myenum.a、myenum.b、myenum.c的枚举;

    我有一个扩展这3个接口的类:X实现A、B、C;

    有一种方法可以像这样在x中实现方法;

    public <T extends Intf> T getType (MyEnum enum)
    

    结果是接口A、B或C,也就是说,只访问方法A()、B()或C()?

    编辑:我想在具有Fluent API的生成器上使用它:

    X var = X.getType(MyEnum.A).a("value").build();
    

    X var = X.getType(MyEnum.B).b("value").build();
    

    但从来没有

    X var = X.getType(MyEnum.A).b("value").build(); //ERROR
    
    3 回复  |  直到 6 年前
        1
  •  1
  •   lexicore    6 年前

    如果我正确地阅读了您的问题,您希望编译时安全

    public <T extends Intf> T getType (MyEnum enum)
    

    返回 A 对于 MyEnum.A , B 对于 MyEnum.B 等。

    如果您使 MyEnum 类泛型。这现在可以用于普通枚举,但它可以用于老式的“类型安全枚举”模式。

    假设我们有三个接口 AA , BB , CC 扩展基本接口 II :

    public interface AA extends II { void a(); }
    public interface BB extends II { void b(); }
    public interface CC extends II { void c(); }
    

    现在上课 TT 实现所有这些接口:

    public class TT implements AA, BB, CC {
    
        @Override
        public void a() { ... }
    
        @Override
        public void b() { ... }
    
        @Override
        public void c() { ... }
    
    }
    

    现在让 EE 是我们的泛型伪枚举类,用以下子类型参数化 :

    public class EE<XX extends II> {
    
        public static final EE<AA> A = new EE<AA>();
        public static final EE<BB> B = new EE<BB>();
        public static final EE<CC> C = new EE<CC>();
    
    }
    

    根据这些定义, getType 方法可以声明如下:

    public <XX extends II> XX getType(EE<XX> enumVal)
    

    此方法只能返回类型参数化 enumVal . 意义

    AA type = tt.getType(EE.A);
    

    有效,但

    BB type = tt.getType(EE.A);
    

    不是。

    实现 获取类型 方法将委托 TT 实例到 AA , BB 科科斯群岛 对应的伪枚举:

    public abstract class EE<XX extends II> {
    
        public static final EE<AA> A = new EE<AA>() {
            @Override
            public <PP extends AA & BB & CC> AA convert(PP instance) {
                return new AA() {
                    public void a() {
                        instance.a();
                    };
                };
            }
        };
        public static final EE<BB> B = new EE<BB>() {
            @Override
            public <PP extends AA & BB & CC> BB convert(PP instance) {
                return new BB() {
                    public void b() {
                        instance.b();
                    };
                };
            }
        };
        public static final EE<CC> C = new EE<CC>() {
            @Override
            public <PP extends AA & BB & CC> CC convert(PP instance) {
                return new CC() {
                    public void c() {
                        instance.c();
                    };
                };
            }
        };
    
        public abstract <PP extends AA & BB & CC> XX convert(PP instance);
    }
    

    你也可以回来 instance 直接,而不封装在匿名内部类中。但是结果可以强制转换到其他接口,从而允许访问其他方法。

    最后,实施 获取类型 微不足道:

    public <XX extends II> XX getType(EE<XX> enumVal) {
        return enumVal.convert(this);
    }
    

    据我所见,编译器不允许

    BB bb = tt.getType(EE.A);
    

    阿尔索

    BB bb = (BB) tt.getType(EE.A);
    bb.b();
    

    不会像“产品”那样工作 ClassCastException 在运行时”。

    缺点是伪枚举构造和 convert .

        2
  •  2
  •   Peter Walser    6 年前

    如@ghostcat建议的那样,可以分派枚举值并返回匹配的实例。

    你也可以 反转查找 如此 每个枚举值都提供适当的实例 属于 Intf :

    变体1 :每个枚举值的单个实例

    public enum MyEnum {
    
        A(new AImpl()),
        B(new BImpl()),
        C(new CImpl());
    
        private Intf instance;
    
        MyEnum2(Intf instance) {
            this.instance = instance;
        }
    
        public <T extends Intf> T getType() {
            return (T) instance;
        }
    }
    

    变体2 :工厂,创建新实例:

    public enum MyEnum {
    
        A(AImpl.class),
        B(BImpl.class),
        C(CImpl.class);
    
        private Class<? extends Intf> type;
    
        MyEnum(Class<? extends Intf> type) {
            this.type = type;
        }
    
        public <T extends Intf> T getType() {
            try {
                return (T) type.newInstance();
            } catch (InstantiationException | IllegalAccessException ex) {
                throw new RuntimeException(ex.getMessage(), ex);
            }
        }
    }
    

    用途:

    A a = MyEnum.A.getType();
    B b = MyEnum.B.getType();
    C c = MyEnum.C.getType();
    
        3
  •  0
  •   GhostCat    6 年前

    假设我们在类X中,您有一个局部泛型参数,可以考虑:

    public <T extends Intf> T getType (MyEnum enumVal) {
      if (enumVal == MyEnum.A) {
        return (A) this;
      if (enumVal == MyEnum.B) {
        return (B) this;
    

    但这样做并没有任何好处。这些演员阵容对主叫方来说并不重要。

    因为编译器在这里不能为您做任何事情。你可以写

    A someA = whatever.getType(someEnum);
    

    但是你也可以写

    B someB = whatever.getType(someEnum);
    

    同样的 someEnum . 编译器会很高兴的。

    如果您想要获得“编译时安全性”的好处,那么您必须以某种方式将参数类型“连接”到结果类型。