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

问题描述:

我试图编写一个具有泛型成员变量但不是泛型的类。具体而言,我想说的是,我有一个“实现与自身相媲美的某种类型”的值列表,以便我可以在该列表上调用排序......我希望这是有道理的。非泛型类中的泛型实例变量

我想要做的最终结果是创建一个类,以便我可以创建一个具有(任何给定类型)数组的类的实例,并让它为该列表生成一个字符串表示形式。在实际的代码,我也通过在课堂上,我传入类型:

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); 

本来我根本不想在课堂上通过,我只是想在价值传递和有代码检查结果数组以挑选值的类......但这也给我带来麻烦。

以下是我的代码,但我不能想出正确的魔力把该变量的类型。

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; 
    } 
} 

上加行错误:

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

结论:

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

的排序行错误

看来,它似乎是Java不能完全处理wh在我想要做的事情上。问题是因为什么,我想说的是:

我想是 可比对自己的项目清单,我 从创建传入的 数据一次创建整个列表。

但是,Java看到我有这个列表,并且无法确定我的情况的所有信息在编译时是否可用,因为我可以尝试稍后将其添加到列表中,并且由于类型擦除,它不能保证安全。如果不将通用类型应用到类中,那么与Java通信的情况实际上是不可能的。

+3

为什么不足以拥有一个实现Comparable的事物列表? – 2010-07-07 03:38:27

+0

并不是说它解决了这个问题,但是可比较的东西并不意味着它与它本身相比。只是实施Comparable并不是说“与自己相比”。 – RHSeeger 2010-07-07 04:31:49

+0

你可以用静态方法来做到这一点。看到我的第二个答案。 – TofuBeer 2010-07-07 05:03:51

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

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

所以把这个还给你正试图在这里做,MyClass的一个实例将保存某种类型的元素,并且您希望能够以静态类型安全的方式插入和删除元素。但同时你不想说出那种类型。那么编译器应该如何将静态地区分出来,区分了一个MyClass实例,该实例持有(说)Integer对象和一个持有String对象的实例?

我甚至不认为你可以用显式动态类型检查来实现这一点。 (我认为这类型擦除意味着,getSortedList()方法的实现不能找出什么实际类型绑定到它的返回类型。)

号真正的解决办法是让MyClass一个泛型类声明的类型参数T ;例如

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

并取出方法级类型参数T从两种方法的声明。

+0

我同意T在这段代码中没有任何上下文,但这是问题所在。我想要做的就是“有一个可以与自身比较的类型列表,以便我可以对其进行分类”。这显然比听起来不像通用的类更难。 – RHSeeger 2010-07-07 04:12:41

+0

这只是艰难的(实际上是不可能的),因为你试图避免使课程本身成为通用的。 – 2010-07-07 04:26:51

+0

虽然如此。从概念上讲,这个类不是通用的。当然,我可以让类通用来解决问题,但我希望有一种方法可以避免它,因为它是一种代码味道。可能是语言所要求的,但是代码味道。 – RHSeeger 2010-07-07 04:30:01

考虑像这样(我所要说的话是不是现实,但它说明了为什么你需要做的,你需要做的事。):

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"); 

如果你能有实例变量/方法的参数不参数化类以上的事情(这是不太现实)止跌”工作。

你想要做什么应该可以用静态方法,但我不认为这是你想要的。

你能解释你为什么要做你想要做的代码吗?也许我们可以想出一个更好的方法来做你想做的事,在语言中起作用。

+0

我在它想要做的事情上添加了更多细节。简而言之,我有一个成员变量,它是我想要排序的列表,但可以是任何单个(可排序)类型的列表。这个类本身不是一个“那种类型”的类,本身就是这个变量。 – RHSeeger 2010-07-07 04:28:24

+0

像新的MyClass (Integer.class,.........);并不罕见。 – TofuBeer 2010-07-07 04:34:54

我相信以下将达到你想要的(比较强的打字)。这将阻止人们将不是来自您的界面的Comparable对象添加到列表中,并允许多个实现。

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); 
    } 
} 
+0

您仍然在那里使用泛型类,再加上您删除了使用系统类(如String)创建实例的功能。 – RHSeeger 2010-07-07 04:33:31

+0

编辑表明,如果主要类不是通用的,它可以轻松工作。我相信目标是只有经批准的可比较数据才能被使用(即字符串不可用于设计)。 – Syntax 2010-07-07 04:38:10

有大量在这个unchecked警告,但在原则上没有必要保持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行为与预期相同,但会抛出一个编译错误,对于不落实Comparable类:

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()); 
    } 
+0

这是非常接近我正在寻找。这就是说,还有另一个难题(有点相关,但使它比我想要处理的任务更复杂),当我这样做的时候,它会让我咬牙切齿。不过,我很欣赏这个答案。 – RHSeeger 2010-07-07 04:42:30

+0

@RHSeeger,因为这基本上回答你的问题,我会欣赏更多的信息,为什么它是不够的... – 2010-07-07 04:52:09

+0

主要问题是,我真的想要成员变量,说:“一些实现类比自己的类型”,而是不仅仅是“某种实现可比较的类型”,对于自我记录代码和其他需要这种值作为输入的代码片段一样。话虽如此,我也讨厌看到未经检查的警告,我的私人见解更多的是Java的糟糕设计。 – RHSeeger 2010-07-07 14:41:53

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; 
    } 
} 

我会做这种方式(我做了一个列表或数组),除非你真的需要的实例变量/方法:

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)); 
    } 
} 

您想要的变量类型约束不能直接表示。你可以引入一个新的类型来弥补这个问题。

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

final MyList<?> values; 

然而,在私人代码段中没有必要是非常安全的。泛型是帮助你澄清你的类型,而不是混淆它们。