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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3191613/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 17:15:46  来源:igfitidea点击:

Generic instance variable in non-generic class

javagenerics

提问by RHSeeger

I'm trying to write a class that has a generic member variable but is not, itself, generic. Specifically, I want to say that I have an List of values of "some type that implements comparable to itself", so that I can call sort on that list... I hope that makes sense.

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

The end result of what I'm trying to do is to create a class such that I can create an instance of said class with an array of (any given type) and have it generate a string representation for that list. In the real code, I also pass in the class of the types I'm passing in:

我试图做的最终结果是创建一个类,以便我可以使用(任何给定类型)的数组创建所述类的实例,并让它为该列表生成一个字符串表示。在实际代码中,我还传入了我传入的类型的类:

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

Originally I didn't even want to pass in the class, I just wanted to pass in the values and have the code examine the resulting array to pick out the class of the values... but that was giving me troubles too.

最初我什至不想传递类,我只想传递值并让代码检查结果数组以挑选值的类......但这也给我带来了麻烦。

The following is the code I have, but I can't come up with the right mojo to put for the variable type.

以下是我拥有的代码,但我无法想出正确的 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;
    }
}

error on variable declaration line:

变量声明行错误:

Syntax error on token "extends", , expected

Any help would be very much appreciated.

任何帮助将不胜感激。

Edit:updated code to use List instead of array, because I'm not sure it can be done with arrays.

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

@Mark:From everything I've read, I really want to say "T is a type that is comparable to itself", not just "T is a type that is comparable". That being said, the following code doesn't work either:

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

error on add line:

添加行错误:

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

error on sort line:

排序行错误:

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

Conclusion:

结论:

What it comes down to, it appears, is that Java can't quite handle what I want to do. The problem is because what I'm trying to say is:

看来,归根结底,Java 不能完全处理我想做的事情。问题是因为我想说的是:

I want a list of items that are comparable against themselves, and I create the whole list at once from the data passed in at creation.

我想要一个可与它们自己进行比较的项目列表,我从创建时传入的数据中立即创建整个列表。

However, Java sees that I have that list and can't nail down that all the information for my situation is available at compile time, since I could try to add things to the list later and, due to type erasure, it can't guarantee that safety. It's not really possible to communicate to Java the conditions involved in my situation without applying the generic type to the class.

但是,Java 看到我有那个列表,并且无法确定我的情况的所有信息在编译时都可用,因为我可以稍后尝试将内容添加到列表中,并且由于类型擦除,它不能保证安全。如果不将泛型类型应用于类,就不可能将我的情况中涉及的条件与 Java 通信。

采纳答案by Stephen C

I think that the simple answer is that you cannot do that. If the type of one of a classes attributes depends on a type parameter, that parameter has to be declared at the class level. And I don't think that it "makes sense" any other way.

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

If Tin your example is not a type parameter of the class, what is it? It cannot be the type parameter of the method, because that type is determined by how the method is called. (If the method is called in different static contexts with different inferred types for T, what is the notional type of Tin the context of the attribute declaration?)

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

So to bring this back to what you are trying to do here, an instance of MyClasswill hold elements of some type, and you want to be able to insert and remove elements in a statically typesafe fashion. But at the same time you don't want to be able to say what that type is. So how is the compiler supposed to staticallydistinguish between a MyClassinstance that holds (say) Integerobjects and one that holds Stringobjects?

因此,为了回到您在这里尝试做的事情,一个实例MyClass将包含某种类型的元素,并且您希望能够以静态类型安全的方式插入和删除元素。但同时,您也不想说出那种类型是什么。那么编译器应该如何静态地区分MyClass持有(比如说)Integer对象的实例和持有String对象的实例呢?

I don't even think you could implement this with explicit dynamic typechecks. (I think that type erasure means that the implementation of the getSortedList()method cannot find out what actual type is bound to its return type.)

我什至不认为您可以使用显式动态类型检查来实现这一点。(我认为类型擦除意味着该getSortedList()方法的实现无法找出与其返回类型绑定的实际类型。)

No. The real solution is to make MyClassa generic class that declares the type parameter T; e.g.

不。真正的解决方案是创建MyClass一个声明类型参数的泛型类T;例如

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

and remove the declaration of the method-level type parameter Tfrom the two methods.

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

回答by TofuBeer

Consider it like this (what I am about to say isn't reality. but it illustrates why you need to do what you need to do):

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

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

When this happens the compiler (DOES NOT REALLY DO WHAT I AM ABOUT TO SAY!) generates the following code:

当这种情况发生时,编译器(并没有真正做我要说的!)生成以下代码:

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

If you were able to have the instance variables/methods parameterized without parameterizing the class the above thing (WHICH IS NOT REALITY!) wouldn't work.

如果您能够在不参数化类的情况下对实例变量/方法进行参数化,则上述事情(这不是现实!)将不起作用。

What you are trying to do should be possible with static methods, but I don't think that is what you want.

您尝试使用静态方法应该可以实现,但我认为这不是您想要的。

Can you explain why you want to do the code you are trying to do? Perhaps we can figure out a better way to do what you want to do that works within the language.

您能解释一下为什么要执行您正在尝试执行的代码吗?也许我们可以找到一种更好的方法来做你想做的事情,并且在语言中起作用。

回答by Syntax

I believe the following will achieve what you want (stronger typing of Comparable). This will prevent people adding Comparable objects which are not from your interface to the list and allow multiple implementations.

我相信以下将实现您想要的(可比较的更强类型)。这将防止人们将不是来自您的界面的 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;
  }
}

edit:

编辑:

I know this may be obvious; but if you do not wish the class to be Generic this solution will also work with:

我知道这可能很明显;但如果您不希望该类为 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);
  }
}

回答by Mark Elliot

There's plenty of unchecked warnings in this, but in principle it's not necessary to keep the Listas anything but something containing things you know are Comparable. You enforce the rules you need to in the constructor, and everything else should be fine. How about something like this:

这里有很多未经检查的警告,但原则上没有必要将 保留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;
    }

}

A quick test using the following shows that for classes that implement Comparable (like Integer and String) MyClassbehaves as expected, but will throw a compilation error for classes that do not implement Comparable:

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

回答by newacct

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

回答by TofuBeer

I'd do it this way (I did it as a list or as an array), unless you really need the instance variable/methods:

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

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

回答by irreputable

the type constraint you want on the variable can't be expressed directly. you can introduce a new type to bridge the problem.

你想要的变量类型约束不能直接表达。您可以引入一种新类型来解决问题。

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

final MyList<?> values;

however, there is no point to be extremely type safe in a private piece of code. Generic is there to help you clarify your types, not to obfuscate them.

然而,在一段私有代码中高度类型安全是没有意义的。泛型是为了帮助您澄清您的类型,而不是混淆它们。