【问题标题】:Java dynamic return type?Java动态返回类型?
【发布时间】:2012-10-14 11:40:36
【问题描述】:

我有一个包含一些 T 类型元素的容器(列表)并且想要过滤它。所以它只包含特定子类型 U 的元素。是否可以设置“动态”返回类型?

示例:

class SomeContainer<T> extends ArrayList<T>{

    public SomeContainer<T> subset(Class c){
        SomeContainer<...here the type of c > output = new SomeContainer<.. also ..>();

        //filter own elements and only add c-objects in the new list

        return output;
    }
}

此时它返回一个泛型类型 T 而不是 c-Class 类型(T 的子类型)的列表。因此我有时会收到以下编译器通知:

Note: SomeContainer.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

因为我想在子类型的对象之后过滤列表并触发特定于子类型的方法,所以我需要一个特定的子类型列表。

【问题讨论】:

  • 警告是因为您使用的是Class 而不是Class&lt;?&gt;。而且我不确定您是否可以在模板参数中使用动态类型。
  • 所以它只包含特定子类型 U 的元素 为什么不用那个特定子类型 U 声明呢?

标签: java generics types


【解决方案1】:

java.lang.Class 是一个自身参数化的泛型类型,所以你可以使用它的类型参数,像这样:

public <U extends T> SomeContainer<U> subset(Class<U> c){
    SomeContainer<U> output = new SomeContainer<U>();
    for (T val : this) {
        if (c.isInstance(val)) {
            output.add(c.cast(val));
        }
    }
    return output;
}

【讨论】:

  • 或者更好,output.add(c.cast(val)) 以避免未经检查的强制转换警告。
  • @IanRoberts 绝对!谢谢你的好建议!
【解决方案2】:

泛型只是编译时的人工制品,因此您的方案无法工作。编译器无法预测在每次执行调用此函数的代码行时您需要什么类。你不能使这个解决方案类型安全除非你有一个非常受限且非常无用的情况,你只使用类文字来调用你的函数。然而,这几乎肯定会破坏它的目的,正如你所说的,动态的。

【讨论】:

    【解决方案3】:

    第一点:您可以通过将Class c 参数替换为Class&lt;T&gt; c 来删除未经检查的操作警告。这可能就是你所需要的......在这种情况下...... =:-)

    第二点: 通常调用 SomeContainer.subset() 的代码会在编译类型时知道类型 U(来自逻辑上下文)。这一定是你的情况,否则你将无法传递 Class c 参数。

    试试:

    class SomeContainer<T> extends ArrayList<T>{
    
        public <U extends T> SomeContainer subset(Class<U> c){
            SomeContainer<U> output = new SomeContainer<U>();  
            // put filtered elements into output
    
            return output;    
        }
    }
    

    看看我在那里做了什么?
    在方法调用中引入了第二种类型的参数:U extends T。 在 Class&lt;U&gt; c 的参数中也使用了这个。
    调用者会调用 like(其中 X 被选择为 T 的子类):

    SomeContainer<X> mySubset = mySomeContainer.subset(X.class);     // type inference
    SomeContainer<X> mySubset = mySomeContainer.<X>subset(X.class);  // type arg specified 
    


    如果您需要比这更动态的东西,通配符可以提供帮助 - 允许传入和传出参数化类型的“家族”:

    public SomeContainer<? extends X> subset(Class<? extends X> c){
    

    这是一个“塑料”函数接口:您可以为任何作为 T 子类的 X 返回 SomeContainer&lt;T&gt;SomeContainer&lt;X&gt;。以下也适用:

    public SomeContainer<? super Z> subset(Class<? extends X> c){
    

    但是,正如另一位发帖者所说,泛型是一种编译时构造,它们在编译期间被生成的非泛型代码替换。这意味着您不能用一行代码动态决定用于实例化泛型类型的类型。但是你可以作弊:如果 T 的子类数量有限,比如 X、Y 和 Z,其中 Z 扩展 Y,Y 扩展 Z,那么你可以使用一个古老的 hacky “if 语句”。试试:

    类 SomeContainer 扩展 ArrayList{

    public SomeContainer<? extends X> subset(Class<? extends X> c){
        SomeContainer<? extends X> output = null;
    
        // would like to use:  "if (c instance of Class<Z>)"
        // but instanceof does not allow generic type arguments
        if (c.getName().equals(Z.class.getName())) {
            SomeContainer<Z> outputZ = new SomeContainer<Z>();
            // put filtered elements into outputZ
            output = outputZ;
        } else if (c.getName().equals(Y.class.getName())) {
            SomeContainer<Y> outputY = new SomeContainer<Y>();
            // put filtered elements into outputZ
            output = outputY;
        } else if (c.getName().equals(X.class.getName())) {
            SomeContainer<X> outputX = new SomeContainer<X>();
            // put filtered elements into outputZ
            output = outputX;
        }
        return output;    
    }
    

    }

    简单! (或不)=:-)

    【讨论】:

      猜你喜欢
      • 2013-08-04
      • 1970-01-01
      • 1970-01-01
      • 2013-08-19
      • 2012-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-19
      • 2011-10-10
      相关资源
      最近更新 更多