代码之家  ›  专栏  ›  技术社区  ›  RHSeeger

非泛型类中的泛型实例变量

  •  10
  • RHSeeger  · 技术社区  · 14 年前

    我正在尝试编写一个类,它有一个泛型成员变量,但本身不是泛型的。具体来说,我想说的是,我有一个“一些实现与自身类似的类型”的值列表,这样我就可以在该列表上调用sort…我希望这是有道理的。

    我要做的最后一件事是创建一个类,这样我就可以用一个数组(任何给定类型)创建该类的实例,并让它为该列表生成一个字符串表示。在真正的代码中,我还传递我要传递的类型的类:

    String s = new MyClass(Integer.class, 1,2,3).asString();
    assertEquals("1 or 2 or 3", s);
    String s = new MyClass(String.class, "c", "b", "a").asString();
    assertEquals("\"a\" or \"b\" or \"c\"", s);
    

    最初我甚至不想在类中传递,我只是想传递值,并让代码检查结果数组以选出值的类…但这也给我带来了麻烦。

    下面是我拥有的代码,但我无法为变量类型找到合适的mojo。

    public class MyClass {
        // This doesn't work as T isn't defined
        final List<T extends Comparable<? super T>> values;
    
        public <T extends Comparable<? super T>> MyClass (T... values) {
            this.values = new ArrayList<T>();
            for(T item : values) {
                this.values.add(item);
            }
        }
    
        public <T extends Comparable<? super T>> List<T> getSortedLst() {
            Collections.sort(this.values);
            return this.values;
        }
    }
    

    变量声明行出错:

    Syntax error on token "extends", , expected
    

    任何帮助都将不胜感激。

    编辑: 更新了使用列表而不是数组的代码,因为我不确定是否可以用数组来完成。

    @马克: 从我读过的每一篇文章来看,我真的想说“T是一种与自身相比较的类型”,而不仅仅是“T是一种与自身相比较的类型”。也就是说,以下代码也不起作用:

    public class MyClass {
        // This doesn't work
        final List<? extends Comparable> values;
    
        public <T extends Comparable> MyClass (T... values) {
            this.values = new ArrayList<T>();
            for(T item : values) {
                this.values.add(item);
            }
        }
    
        public <T extends Comparable> List<T> getSortedLst() {
            Collections.sort(this.values);
            return this.values;
        }
    }
    

    添加行出错:

    The method add(capture#2-of ? extends Comparable) in the type List<capture#2-of ? extends Comparable> is not applicable for the arguments (T)
    

    排序行出错:

    Type mismatch: cannot convert from List<capture#4-of ? extends Comparable> to List<T>
    

    结论:

    看来,Java不能很好地处理我想做的事情。问题是因为我想说的是:

    我想要一份清单 与自己相比,我 立即从创建整个列表 创建时传入的数据。

    但是,Java看到我有这个列表,并且不能确定我的情况的所有信息在编译时是可用的,因为我可以稍后尝试添加到列表中,并且由于类型擦除,它不能保证安全。不可能将Java中涉及的条件传递给Java,而不将泛型类型应用到类中。

    7 回复  |  直到 11 年前
        1
  •  7
  •   Stephen C    14 年前

    我认为简单的答案是你不能这样做。如果某个类属性的类型依赖于类型参数,则必须在类级别声明该参数。我不认为它“有意义”任何其他方式。

    如果 T 在您的示例中不是类的类型参数,它是什么?它不能是方法的类型参数,因为该类型由方法的调用方式决定。(如果在具有不同推断类型的不同静态上下文中调用该方法, T ,什么是概念类型的 T 在属性声明的上下文中?)

    所以为了让这个回到你想在这里做的事情,举个例子 MyClass 将保存某些类型的元素,并且您希望能够以静态类型安全的方式插入和删除元素。但同时你也不想说出那是什么类型。那么编译器应该怎么做呢 静态地 区分 类名 持有的实例 Integer 对象和一个 String 物体?

    我甚至不认为您可以通过显式的动态类型检查来实现这一点。(我认为类型擦除意味着 getSortedList() 方法无法确定实际类型绑定到其返回类型。)

    不,真正的解决办法是 类名 声明类型参数的泛型类 T 例如

    public class MyClass <T extends Comparable<T>> {
    

    并移除方法级类型参数的声明 T 从这两种方法。

        2
  •  2
  •   Mark Elliot    14 年前

    这里面有很多未经检查的警告,但原则上没有必要保留 List 除了包含你所知道的东西 Comparable . 在构造函数中强制执行所需的规则,其他一切都应该是好的。像这样的怎么样:

    public class MyClass {
    
        final private List<Comparable> values;
    
        public <T extends Comparable<? super T>>MyClass(T... values){
            this.values = new ArrayList<Comparable>();
            for(T item : values) {
                this.values.add(item);
            }
        }
    
        public <T extends Comparable<? super T>> List<T> getSortedLst() {
            Collections.sort(this.values);
            return (List<T>)this.values;
        }
    
    }
    

    下面的一个快速测试表明,对于实现可比较的类(如integer和string) MyClass 行为如预期,但将为未实现的类引发编译错误 可比的 :

        class Junk { }
    
        public static void main(String[] args){
            MyClass s = new MyClass(1,2,3);
            System.out.println(s.getSortedLst());
    
            MyClass a = new MyClass("c", "a", "b");
            System.out.println(a.getSortedLst());
    
            MyClass c = new MyClass(new Junk());
        }
    
        3
  •  1
  •   TofuBeer    14 年前

    这样想(我要说的不是事实)。但它说明了为什么你需要做你需要做的事情):

    class Foo<T>
    {
        private T value;
    
        T getValue() { return value; }
        void setValue(T val) {value = val; }
    }
    
    // some code that uses the above class
    
    Foo<Integer> iFoo = new Foo<Integer>();
    Foo<String> sFoo = new Foo<String>();
    iFoo.setValue(5);
    sFoo.setValue("Hello");
    

    当发生这种情况时,编译器(不会真的做我要说的事情!)生成以下代码:

    class IntegerFoo
    {
        private Integer value;
    
        Integer getValue() { return value; }
        void setValue(Integer val) {value = val; }
    }
    
    class StringFoo
    {
        private String value;
    
        String getValue() { return value; }
        void setValue(String val) {value = val; }
    }
    
    // some code that uses the above class
    
    IntegerFoo iFoo = new IntegerFoo();
    StringFoo< sFoo = new StringFoo();
    iFoo.setValue(5);
    sFoo.setValue("Hello");
    

    如果您能够在不参数化类的情况下对实例变量/方法进行参数化,那么上面的内容(这不是实际情况!)不会起作用的。

    您所要做的应该可以使用静态方法,但我不认为这是您想要的。

    你能解释一下为什么你要做你想做的代码吗?也许我们可以找到一个更好的方法来做你想做的事情,这在语言中是有效的。

        4
  •  1
  •   Syntax    14 年前

    我相信下面的内容会达到你想要的效果(比较强的输入法)。这将阻止人们将不在接口中的可比较对象添加到列表中,并允许多个实现。

    public class test<T extends ComparableType> {
    
    
    final List<T> values = new ArrayList<T>();
      public test (T... values) {
          for(T item : values) {
              this.values.add(item);
          }
      }
    
      public List<T> getSortedLst() {
          Collections.sort(this.values);
          return Collections.unmodifiableList(this.values);
      }
    }
    
    public interface ComparableType extends Comparable<ComparableType> {}
    
    public class ConcreteComparableA implements ComparableType {
      @Override
      public int compareTo(ComparableType o) {
        return 0;
      }
    }
    
    public class ConcreteComparableB implements ComparableType {
      @Override
      public int compareTo(ComparableType o) {
        return 0;
      }
    }
    

    编辑:

    我知道这可能很明显;但是如果您不希望类是通用的,那么这个解决方案也可以用于:

     public class test {
      final List<ComparableType> values = new ArrayList<ComparableType>();
    
      public test (ComparableType... values) {
          for(ComparableType item : values) {
              this.values.add(item);
          }
      }
    
      public List<ComparableType> getSortedLst() {
          Collections.sort(this.values);
          return Collections.unmodifiableList(this.values);
      }
    }
    
        5
  •  1
  •   TofuBeer    14 年前

    我会这样做(我是以列表或数组的形式进行的),除非您真正需要实例变量/方法:

    import java.lang.reflect.Array;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.List;
    
    
    public class MyClass
    {
        public static <T extends Comparable<T>> List<T> asSortedList(final T ... vals)
        {
            final List<T> temp;
    
            temp = new ArrayList<T>(vals.length);
            temp.addAll(Arrays.asList(vals));
            Collections.sort(temp);
    
            return (Collections.unmodifiableList(temp));
        }
    
        public static <T extends Comparable<T>> T[] asSortedArray(final Class<?> clazz,
                                                                  final T ...    vals)
        {
            final T[] temp;
    
            temp = (T[])Array.newInstance(clazz,
                                     vals.length);
            System.arraycopy(vals,
                             0,
                             temp,
                             0,
                             vals.length);
            Arrays.sort(temp);
    
            return (temp);
        }
    
        public static void main(final String[] argv)
        {
            final List<String> list;
            final String[]     array;
    
            list = MyClass2.asSortedList("c", "a", "b");
            System.out.println(list);
    
            array = MyClass2.asSortedArray(String.class, "z", "y", "x");
            System.out.println(Arrays.deepToString(array));
        }
    }
    
        6
  •  0
  •   irreputable    14 年前

    无法直接表示您对变量所需的类型约束。你可以引入一种新的类型来解决这个问题。

    static class MyList<T extends Comparable<? super T>> extends ArrayList<T>{}
    
    final MyList<?> values;
    

    但是,在一段私有代码中,没有必要使用非常安全的类型。通用是帮助你澄清你的类型,而不是混淆它们。

        7
  •  -1
  •   newacct    14 年前
    public class MyClass<T extends Comparable<? super T>> {
        // This doesn't work as T isn't defined
        final List<T> values;
    
        public MyClass (T... values) {
            this.values = new ArrayList<T>(Arrays.asList(values));
        }
    
        public List<T> getSortedLst() {
            Collections.sort(this.values);
            return this.values;
        }
    }