【问题标题】:Have a class implement an unspecified generic interface in java让一个类在java中实现一个未指定的泛型接口
【发布时间】:2010-09-29 10:08:46
【问题描述】:

我有以下“通用”问题

这是我的通用消息接口。

public interface Message<P,R> {  
    void setParams(P Params); // and the corresponding getter  
    void setResults(R results);  
}  

为了简单起见,我想通过预定义参数和结果类型来指定一些(接口)消息。这样,如果您使用消息 X,您就会知道参数和结果类型是什么。

public interface MyMessage extends Message<String,String> {}  

这很好,现在我想要一个可以实现任何消息的通用基类。

public class BaseMessage<P,R> implements Message<P,R> {
    P param;
    R results;
    // base implementation
}

现在上面的问题是我不能有一个实现MyMessage的BaseMessage。

多次尝试

public class BaseMessage<T extends BaseMessage<P,R>> implements Message<P,R> 

public class BaseMessage<? extends BaseMessage<P,R>> implements Message<P,R> 

将不起作用,因为编译器抱怨 P 和 R 未绑定。 将它们作为附加参数引入再次起作用,但此时生成的类将无法转换为 BaseMessage 或所需的 MyMessage。

public class BaseMessage<P,R,T extends BaseMessage<P,R>> implements Message<P,R> 

我想要一个通用工厂方法,它会被赋予消息类、参数和结果对象,并将返回一个实现 MyMessage 接口的 BaseMessage 实例。如下所示

public static <P, R, T extends Message<P,R>> T factory(Class<T> clazz,P p,R r) {
    T a = (T) new BaseMessage<P,R>();
    // create a BaseMessage which which Implements T
    a.setParams(p);
    a.setResults(r);
    return a;
}

有没有人遇到过类似的问题,或者这在 Java 中是不可能的?

谢谢!

【问题讨论】:

  • public interface MyMessage extends BaseMessage 这行不通,在您的示例中 BaseMessage 是一个类。
  • 感谢您的评论,它确实应该阅读 public interface MyMessage extends Message {}

标签: java generics


【解决方案1】:

这样的泛型工厂恐怕不行,因为你不能像这样实例化泛型类型

T a = new BaseMessage<T>();

你必须告诉编译器确切的泛型类型参数。

可以做的是创建每个具体消息类型的实例,并将它们存储在一个映射中,并以相应的类标记作为键。然后工厂方法可以查找正确的消息并直接返回它,或者(如果您需要单独的实例)使用消息对象作为prototype 来创建要返回的实例。

在我们当前的项目中,我们有一个有点相似的设计,不同之处在于具体的子类是每个现有的类,具有不同的名称和(不幸的是,大部分 - 但不是完全 - 重复)实现。所以我们可以使用标准的类加载和 Spring 来实例化它们。

【讨论】:

    【解决方案2】:

    您尚未指定工厂如何决定使用哪个实现类,或者实现类是否需要任何自定义初始化逻辑或参数。假设只需使用默认构造函数实例化注册的实现类就足够了,您可以这样做:

    interface Message<P, R> {
        void setParams(P Params); // and the corresponding getter
    
        void setResults(R results);
    }
    
    interface StringMessage extends Message<String, String> {
    }
    
    abstract class MessageImpl<P, R> implements Message<P, R> {
        P param;
        R results;
    }
    
    abstract class StringMessageImpl extends MessageImpl<String, String> implements
            StringMessage {
    
    }
    class ImplementationMap {
        private Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>>();
    
        public <T> void register(Class<T> interfaceClass, Class<? extends T> implementationClass) {
            map.put(interfaceClass, implementationClass);
        }
    
        public <T> Class<? extends T> get(Class<T> interfaceClass) {
            return map.get(interfaceClass).asSubclass(interfaceClass);
        }
    }
    
    public class Test {
        static final ImplementationMap messageImplementors = new ImplementationMap() {{
            register(StringMessage.class, StringMessageImpl.class);
        }};
    
        static <P, R, M extends Message<P, R>> M create(Class<M> clazz, P p, R r)
                throws Exception {
            M m = messageImplementors.get(clazz).newInstance();
            m.setParams(p);
            m.setResults(r);
            return m;
        }
    
        public static void main(String[] args) throws Exception {
            StringMessage m = create(StringMessageImpl.class, "p", "r");
        }
    }
    

    PS:你确定你没有过度设计吗?您真的需要 DTO 的并行类型层次结构吗?

    【讨论】:

    • 我猜M m = clazz.newInstance(); 将不起作用,因为 M 应该是一个接口。
    • 是吗? (OP 写道“将收到消息 class”)
    • 嗯,它应该是接口,正如 Arian 指出的那样,给工厂并且应该返回一个实现类......
    • 假设你的意思是一个对象实现类,我已经更新了我的答案。
    【解决方案3】:

    鉴于接口 interface Message&lt;P,R&gt;interface MyMessage extends Message&lt;String, String&gt; 以及类 class BaseMessage&lt;P,R&gt; implements Message&lt;P,R&gt;,我看到的选项如下:

    1. 不要使用MyMessage 并将所有实例替换为Message&lt;String, String&gt;。您的工厂将是

      public <P,R> Message<P, R> newMessage() { 
          return new BaseMessage<P,R>();
      }
      
    2. 有一堂课

      class MyBaseMessage extends BaseMessage<String, String> implemens MyMessage
      

      工厂将是

      public <M extends Message> M newMessage(Class<M> m) { 
          if (m.isAssignableFrom(MyBaseMessage.class)) {
              return (M) new MyBaseMessage();
          }
          if (m.isAssignableFrom(BaseMessage.class)) {
              return (M) new BaseMessage();
          }
          return null;
      }
      
    3. 不要使用接口。

    4. 不要使用工厂。

    5. (如果一切都坏了)让你的工厂成为

      public <M extends Message> M newMessage(Class<M> m) { 
          try {
              return getImplClass(m).newInstance();
          } catch (Exception ex) { /* do proper error handling */ }
      }
      

      getImplClass():在运行时使用javassist创建一个实现类,实现接口M并扩展BaseMessage。

    【讨论】:

      猜你喜欢
      • 2021-10-09
      • 2010-11-20
      • 2020-11-28
      • 2010-11-10
      • 2019-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-27
      相关资源
      最近更新 更多