泛型类中的广泛类型擦除

时间:2019-02-25 20:06:26

标签: java

一段时间以来,我以为我已经了解类型擦除和泛型类型的工作原理,但今天我偶然发现了在类中使用泛型类型并且即使在与泛型无关的类型上也发生了意外的类型擦除的情况。考虑以下示例类:

import java.util.Optional;

class TheClass<T> {
    Optional<Something> getSomething() {
        return Optional.of(new Something());
    }

    class Something {
        int getId() {
            return 1;
        }
    }
}

值得注意的是,它具有完全不使用的泛型。该类的用法如下:

public class Main {
    private static TheClass clazz = new TheClass<>();

    public static void main(String[] args) {
        clazz.getSomething().ifPresent(s -> System.out.println(((TheClass.Something) s).getId()));
    }
}

clazz.getSomething()。ifPresent产生警告(未经检查的对ifPresent的调用),以及内部调用是否需要当前强制转换。如果将通用T从抽象类中删除,则代码将按预期工作,而不会发出警告并进行强制转换。

观察到行为的原因是什么?为什么Javac似乎从TheClass获得所有类型?有什么解决方法可以避免强制转换参数和抑制未检查的警告?

2 个答案:

答案 0 :(得分:2)

那呢?

public class Main {
    private static TheClass<?> clazz = new TheClass<>();

    public static void main(String[] args) {
        clazz.getSomething().ifPresent(s -> System.out.println(((TheClass.Something) s).getId()));
    }
}

警告消失。 (而且演员表是不必要的,因此可以将其删除。)

原始类型的非静态内部类本身就是原始类型,如JLS § 4.8所述。

  

更准确地说,原始类型定义为以下类型之一:

     
      
  • 通过引用通用类型声明的名称而形成的引用类型,而没有随附的类型参数列表。
  •   
  • 其元素类型为原始类型的数组类型。
  •   
  • 未从R的超类或超接口继承的原始类型R的非静态成员类型。
  •   

这与TheClass有关:是原始类型,因为您已将其定义为TheClass(没有泛型类型)。这意味着Optional也是原始的。编译器开始抱怨Optional是原始的,除了抱怨原始类型本身。

答案 1 :(得分:2)

问题是您的clazz静态实例仍然是 raw

即使您指定了菱形运算符,编译器也无法使用泛型类型,因为左侧是原始的。

原始类会丢失所有泛型。您可以选择两种方法。

  1. 在这种情况下,请使用特定的类型Something(但是,由于必须在静态上下文中引用它,因此必须将其声明为static)。在本练习中,我将类Something设为静态,
  static class Something {
        int getId() {
            return 1;
        }
  }
    private static TheClass<Something> clazz = new TheClass<>();

    public static void main(String[] args) {
        clazz.getSomething().ifPresent(s -> System.out.println(s.getId()));
    }
  1. 或使用通配符作为MC Emperor的答案。