代码之家  ›  专栏  ›  技术社区  ›  oxbow_lakes

为什么我要关心Java没有具体化的泛型?

  •  101
  • oxbow_lakes  · 技术社区  · 15 年前

    reified generics 但是,当被催促时,这位候选人实际上无法告诉我,如果他们在场,他本可以取得什么样的成就。

    显然,因为Java中允许原始类型(以及不安全的检查),所以有可能破坏泛型并最终导致错误 List<Integer> String s如果类型信息具体化,这显然是不可能的; !

    人们能举一些例子吗 他们真的很想这么做 ,是否有具体化的仿制药?我是说,很明显你可以得到a型 List 在运行时-但您会如何处理它?

    public <T> void foo(List<T> l) {
       if (l.getGenericType() == Integer.class) {
           //yeah baby! err, what now?
    

    :快速更新此内容,因为答案似乎主要关注是否需要通过 Class 作为参数(例如 EnumSet.noneOf(TimeUnit.class) ). 我在寻找更多的东西 这是不可能的 . 例如:

    List<?> l1 = api.gimmeAList();
    List<?> l2 = api.gimmeAnotherList();
    
    if (l1.getGenericType().isAssignableFrom(l2.getGenericType())) {
        l1.addAll(l2); //why on earth would I be doing this anyway?
    
    13 回复  |  直到 15 年前
        1
  •  81
  •   Community CDub    8 年前

    从我几次遇到这种“需要”的情况来看,它最终归结为以下结构:

    public class Foo<T> {
    
        private T t;
    
        public Foo() {
            this.t = new T(); // Help?
        }
    
    }
    

    T 有一个 构造器。您甚至可以通过以下方式获取运行时类型: typeof(T) 并通过 Type.GetConstructor() .

    常见的Java解决方案是通过 Class<T> 作为论据。

    public class Foo<T> {
    
        private T t;
    
        public Foo(Class<T> cls) throws Exception {
            this.t = cls.newInstance();
        }
    
    }
    

    try-catch 为简洁起见,省略了)

        2
  •  101
  •   RHSeeger    15 年前

    最让我头疼的是无法利用跨多个泛型类型的多个分派。以下是不可能的,在许多情况下,这将是最好的解决方案:

    public void my_method(List<String> input) { ... }
    public void my_method(List<Integer> input) { ... }
    
        3
  •  35
  •   gustafc    15 年前

    List<String> myFriends = new ArrayList();
    myFriends.add("Alice");
    getSession().put("friends", myFriends);
    // later, elsewhere
    List<Friend> myFriends = (List<Friend>) getSession().get("friends");
    myFriends.add(new Friend("Bob")); // works like a charm!
    // and so...
    List<String> myFriends = (List<String>) getSession().get("friends");
    for (String friend : myFriends) print(friend); // ClassCastException, wtf!? 
    

    也, 抽象会少泄漏 -至少是那些可能对其类型参数的运行时信息感兴趣的人。今天,如果您需要任何类型的关于其中一个泛型参数类型的运行时信息,您必须传递其 Class

        4
  •  27
  •   Turnor    15 年前

    您可以在代码中创建泛型数组。

    public <T> static void DoStuff() {
        T[] myArray = new T[42]; // No can do
    }
    
        5
  •  17
  •   user2684301    11 年前

    这是一个老生常谈的问题,有很多答案,但我认为现有的答案是错误的。

    • 这是一个可怕的装箱要求,并且在原语和引用类型之间存在断开连接。这与物化或类型擦除没有直接关系。C#/Scala解决这个问题。
    • 没有自我类型。出于这个原因,JavaFX8不得不删除“构建器”。绝对与类型擦除无关。Scala解决了这个问题,但不确定C#。
    • 没有声明边类型差异。C#4.0/Scala有这个。绝对与类型擦除无关。
    • 不能超载 void method(List<A> l) method(List<B> l) . 这是由于类型擦除,但非常小。
    • 不支持运行时类型反射。这是类型擦除的核心。如果您喜欢在编译时验证和证明尽可能多的程序逻辑的超级高级编译器,那么应该尽可能少地使用反射,并且这种类型的类型擦除不应该困扰您。如果您喜欢更零碎、脚本化、动态的类型编程,并且不太关心编译器如何证明尽可能多的逻辑是正确的,那么您需要更好的反射,并且修复类型擦除非常重要。
        6
  •  14
  •   Cogman    11 年前

    通过具体化,序列化将更加直接。我们想要的是

    deserialize(thingy, List<Integer>.class);
    

    我们要做的是

    deserialize(thing, new TypeReference<List<Integer>>(){});
    

    在有些情况下,说这样的话会很有帮助

    public <T> void doThings(List<T> thingy) {
        if (T instanceof Q)
          doCrazyness();
      }
    

    这些东西不常咬人,但一旦发生就会咬人。

        7
  •  11
  •   omiel    11 年前

    我对Java Geneircs的接触是相当有限的,除了其他答案已经提到的几点之外,书中还解释了一个场景 Java Generics and Collections ,作者Maurice Naftalin和Philip Walder,其中具体化的仿制药很有用。

    例如,以下表单的声明无效。

    class ParametericException<T> extends Exception // compile error
    

    这是因为 抓住 子句检查抛出的异常是否与给定类型匹配。此检查与实例测试执行的检查相同,并且由于类型不可重新定义,因此上述语句形式无效。

    如果上述代码有效,则可以通过以下方式处理异常:

    try {
         throw new ParametericException<Integer>(42);
    } catch (ParametericException<Integer> e) { // compile error
      ...
    }
    

    书还提到,如果java泛型定义的方式类似C++模板的方式 定义(扩展)它可能导致更高效的实现,因为这提供了更多 优化的机会。但是没有提供比这更多的解释,所以来自知识渊博的人的任何解释(指针)都会很有帮助。

        8
  •  8
  •   Hank Gay    15 年前

    如果将泛型具体化,数组可能会更好地处理泛型。

        9
  •  5
  •   James B    15 年前

    API看起来像 Iterator<T> 其中T是某种类型,可以仅使用构造函数中的字符串构造。迭代器然后查看从sql查询返回的字符串,然后尝试将其与T类型的构造函数匹配。

    基本上,我认为它使编写API(而不仅仅是编写应用程序)变得更容易,因为您可以从对象中推断出更多信息,因此需要更少的配置……直到我看到注释被用于spring或xstream之类的东西,而不是大量的配置中,我才理解注释的含义。

        10
  •  5
  •   kvb    15 年前

    在编写能够反映参数化类型的框架时,还存在几种类型的问题。当然,这可以通过在运行时传递类对象来解决,但这会模糊API,并给框架用户带来额外负担。

        11
  •  2
  •   Bozho    15 年前

    我的观点是,泛型是简单的 这节省了大量的重复铸造。

        12
  •  1
  •   ExCodeCowboy    8 年前

        public interface Service<KEY,VALUE> {
               VALUE get(KEY key);
        }
    
        public class PersonService implements Service<Long, Person>,
            Service<String, Person> //Can not do!!
    
        13
  •  0
  •   Adam    13 年前

    今天我发现了一个问题:没有具体化,如果你编写一个方法来接受一个varargs的泛型项列表。。。调用者可能认为他们是类型安全的,但不小心传入了任何旧的crud,并破坏了您的方法。

    (注:我可能在这里犯了一个错误,但在谷歌上搜索“泛型变量”,上面的内容似乎正是你所期望的。使这成为一个实际问题的是类的使用,我认为-调用者似乎不太小心:()


    例如,我正在使用一个范例,它使用类对象作为映射中的一个键(它比简单的映射更复杂,但从概念上讲,这就是正在发生的事情)。

    public <T extends Component> Set<UUID> getEntitiesPossessingComponent( Class<T> componentType)
        {
            // find the entities that are mapped (somehow) from that class. Very type-safe
        }
    

    e、 没有Java泛型中的具体化,它接受任何“类”对象。这只是之前代码的一个小小扩展:

    public <T extends Component> Set<UUID> getEntitiesPossessingComponents( Class<T>... componentType )
        {
            // find the entities that are mapped (somehow) to ALL of those classes
        }
    

    上述方法必须在一个单独的项目中被写出数千次,因此人为错误的可能性变得很高。调试错误被证明是“不有趣的”。我目前正试图找到一个替代方案,但希望不大。