【问题标题】:Calling generic function with two different generic arguments still compiles使用两个不同的泛型参数调用泛型函数仍然可以编译
【发布时间】:2012-04-13 07:08:15
【问题描述】:

下面的代码怎么可能编译?据我所见,count 函数是用两种不同的类型调用的,但编译器并没有抱怨,而是愉快地编译了这段代码。

public class Test {
        public static <T> int count(T[] x,T y){
                int count = 0;
                for(int i=0; i < x.length; i++){
                        if(x[i] == y) count ++;
                }
                return count;  
        }
        public static void main(String[] args) {
                Integer [] data = {1,2,3,1,4};
                String value = "1";
                int r =count(data,value);
                System.out.println( r + " - " + value);
        }
}

【问题讨论】:

  • 你必须明白Java中的泛型只是一个视觉技巧。通过编译器,类型被删除为 Object。谷歌“Java 类型擦除”来查找脏细节。
  • 确实如此。我或多或少知道发生了什么,但我想知道为什么。我现在意识到,正如你所说,它(泛型)“只是一个视觉技巧”(我什至称之为肮脏)。

标签: java generics compilation compiler-errors


【解决方案1】:

通过一次传递两个对象,您对T 施加了太多约束。这“强制”编译器推断Object。幸运的是,有一个简单的解决方法——只传递一个对象。以下将产生预期的错误。

public static void main(String[] args) {
    Integer[] data = { 1, 2, 3, 4 };
    String value = "1";
    int r = count(value).in(data);
    System.out.println(r + " - " + value);
}

public static <T> Counter<T> count(T obj) {
    return new Counter<T>(obj);
}

public static class Counter<T> {
    private final T obj;

    Counter(T obj) {
        this.obj = obj;
    }

    public int in(T[] array) {
        return in(Arrays.asList(array));
    }

    public int in(Iterable<? extends T> iterable) {
        int count = 0;
        for (T element : iterable) {
            if (element == obj) {
                ++count;
            }
        }
        return count;
    }
}

【讨论】:

  • 我对让代码正常工作并不是很感兴趣(我知道这很丑)。我主要想知道 为什么 它甚至编译。不过有趣的解决方法:-)
  • 这是一个非常聪明的解决方案!
【解决方案2】:

如果您将呼叫更改为:

int r = Test.<Integer>count(data, value);

你会看到编译器抱怨。

【讨论】:

    【解决方案3】:

    在这种情况下,T 是无用的。您可以将签名更改为public static int count(Object[] x, Object y),而不会对编译器允许它接受的参数产生任何影响。 (您可以看到Arrays.fill() 的签名将其用作签名。)

    如果我们考虑更简单的情况,即您只有T 类型的参数,您可以看到,因为T 的任何实例也是其超类的实例,T 总是可以推断为是它的上限,它仍然会接受与以前相同的参数类型。因此我们可以去掉T,而使用它的上限(在本例中为Object)。

    Java 中的数组工作方式相同:数组是协变的,这意味着如果ST 的子类,S[]T[] 的子类。因此,与上述相同的参数适用——如果您只有TT[] 类型的参数,T 可以替换为其上限。

    (请注意,这不适用于非协变或逆变的泛型类型:List&lt;S&gt; 不是List&lt;T&gt; 的子类型。)

    【讨论】:

      【解决方案4】:

      类型并没有太大的不同——它们都是 java.lang.Object 的子类。所以编译器在这种情况下假设 T 是 Object。

      【讨论】:

        【解决方案5】:

        T 被强制向上转换为 ObjectInteger[] 可以向上转换为Object[]String 可以向上转换为Object,并进行类型检查。

        【讨论】:

        • 除此之外:变量r 确实是原始类型(不是对象),但这里涉及到自动装箱,所以它可以工作:)。
        猜你喜欢
        • 1970-01-01
        • 2017-01-28
        • 1970-01-01
        • 2018-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-12
        • 1970-01-01
        相关资源
        最近更新 更多