繁体   English   中英

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

[英]Generic instance variable in non-generic class

我正在尝试编写一个具有泛型成员变量的类,但它本身并不是通用的。 具体来说,我想说我有一个“可以实现与自身相似的某种类型”的值列表,因此我可以在该列表上调用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

任何帮助将非常感谢。

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

@Mark:从我读过的所有内容来看,我真的想说“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传达我的情况所涉及的条件。

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

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

因此,为了使这回到你想要做的事情, MyClass一个实例将保存某种类型的元素,并且你希望能够以静态类型安全的方式插入和删除元素。 但与此同时,你不想说出那种类型。 那么编译器应该如何静态区分保存(比如说) Integer对象的MyClass实例和保存String对象的实例?

我甚至认为你不能用明确的动态类型检查来实现它。 (我认为类型擦除意味着getSortedList()方法的实现无法找出绑定到其返回类型的实际类型。)

不。真正的解决方案是使MyClass成为声明类型参数T的泛型类; 例如

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

并从两个方法中删除方法级类型参数T的声明。

有很多在这个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;
    }

}

使用以下内容进行快速测试表明,对于实现Comparable的类(如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());
    }

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

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

如果你能够在没有参数化类的情况下参数化实例变量/方法,那么上面的东西(这不是现实!)将无法工作。

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

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

我相信以下内容将实现您的目标(更强大的Comparable类型)。 这将阻止人们将不属于您的接口的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;
  }
}

编辑:

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

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

我这样做(我把它作为列表或数组),除非你真的需要实例变量/方法:

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;

但是,在私有代码中没有必要非常安全。 Generic可以帮助您澄清类型,而不是混淆它们。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM