【问题标题】:Java generics (template) specialization possible (overriding template types with specific types)可能的 Java 泛型(模板)特化(用特定类型覆盖模板类型)
【发布时间】:2020-12-04 11:15:50
【问题描述】:

我想知道在 Java 中专门化泛型类型的选项是什么,即在模板类中对某些类型进行特定的覆盖。

在我的情况下,我是一个泛型类(T 类型),通常返回 null,但是当 T 是 String 类型时返回“”(空字符串),或者当它是 Integer 类型时返回 0(零)等.

仅提供特定于类型的方法重载会产生“方法不明确”错误:

例如:

public class Hacking {

  public static void main(String[] args) {
    Bar<Integer> barInt = new Bar<Integer>();
    Bar<String> barString = new Bar<String>();

    // OK, returns null
    System.out.println(barInt.get(new Integer(4)));

    // ERROR: The method get(String) is ambiguous for the type Bar<String>
    System.out.println(barString.get(new String("foo")));
  }

  public static class Bar<T> {

    public T get(T x) {
      return null;
    }

    public String get(String x) {
      return "";
    }  
  }
}

是用特定类型子类化泛型类的唯一选择吗(参见下面示例中的 StringBar?

  public static void main(String[] args) {
    Bar<Integer> barInt = new Bar<Integer>();
    StringBar barString2 = new StringBar();

    // OK, returns null
    System.out.println(barInt.get());

    // OK, returns ""
    System.out.println(barString2.get());
  }

  public static class Bar<T> {

    public T get() {
      return null;
    }
  }

  public static class StringBar extends Bar<String> {
    public String get() {
      return "";
    }
  }
}

这是唯一的方法吗,必须为我想要专门化的每种类型创建一个子类而不是在 Bar 类中重载 get() 有点痛苦。

我猜我可以检查 Bar.get() 方法中的 instanceof,例如 得到(T t) { if (t instanceof String) return ""; if (t instanceof Integer) 返回 0; 否则返回空值; }

但是,我被教导要尽可能避免使用 instanceof 并使用多态性。

【问题讨论】:

  • 请注意,我来自 C++ 世界,通过方法重载可以轻松实现 C++ 专业化
  • Java 泛型与 C++ 模板有很大不同,因为泛型是通过擦除实现的。看看这个帖子,可能会让你明白很多:stackoverflow.com/questions/313584/…
  • 我知道实现的差异,但我想知道如何克服 Java 泛型(特别是专业化)中的缺陷(对于我的用例)

标签: java generics


【解决方案1】:

综合考虑,一致认为问题中提到的 StringBar 方法是唯一可行的方法。

  public static class StringBar extends Bar<String> {
    public String get() {
      return "";
    }
  }

【讨论】:

  • 我已经创建并接受了我自己的答案,因为尽管其他答案有些正确并得出了这个结论,但它们都包含误导点或以某种方式误解了问题。
【解决方案2】:

在这方面,Java 中的泛型与 C++ 中的模板非常不同。不可能像 C++ 那样编写特定版本的泛型类来针对特定情况做不同的事情。在运行时也无法确定 T 是什么——这是因为该信息没有传递到字节码(目标代码)中,因此在运行时甚至不存在。这是由于所谓的“类型擦除”。

BarString 和 BarInt 是执行此操作的明显方式,但您可以进行一些改进。例如,您可以编写一个通用 Bar 来覆盖常见情况,然后编写专门的 BarString 和 BarInt 来实现特殊情况。确保实例只能通过工厂创建,该工厂采用要处理的对象的

class Bar<T> {
  class BarString extends Bar<String> {
    // specialist code goes here
  }


static Bar<T> createBar(Class<T> clazz) {
  if (clazz==String.class) {
    return new BarString();
  } else {
    return new Bar<T>;
}

这可能无法编译,但我没有时间制定确切的语法。它确实说明了原理。

【讨论】:

    【解决方案3】:

    编译器实际上是正确的,因为下面的代码在编译时是编译时检查的(Bar&lt;String&gt; barString = new Bar&lt;String&gt;();),来自

    public static class Bar<T> {
    
        public T get(T x) {
          return null;
        }
    
        public String get(String x) {
          return "";
        }  
      }
    

    public static class Bar<String> {
    
        public String get(String x) {
          return null;
        }
    
        public String get(String x) {
          return "";
        }  
      }
    

    而且是模棱两可的,因为你不能有 2 个具有相同返回类型和相同参数参数的相同方法。

    查看 Jon Skeet 的解释:


    您可以继承 Bar&lt;T&gt; 并创建 StringBar(注意我删除了 static 关键字)并覆盖 get() 方法。

    public class BarString extends Bar<String> {
    
        @Override
        public String get(String x) {
            return "";
        }
    }
    

    【讨论】:

    • 是的,我知道编译器为什么会抛出错误——这已经在 SO 上得到了充分的介绍。我想知道的是 工作的各种解决方案。我已经建议了子类化(StringBar)方法,我想知道是否还有其他专门化的方法。
    • 我的建议是删除public String get(String x) 方法。如果你有Bar&lt;String&gt; barString,为什么需要它?
    • barString 无法正常工作。我希望 barString.get() 返回空字符串,而不是 null。
    • 您的 BarString 的所有意图和目的都与我的问题中的 StringBar 相同。 (除了 String 参数。)@Override 确实使意图更加清晰
    【解决方案4】:

    Java 中的泛型不是为专业化而设计的。它们是为泛化而设计的!如果你想专攻某些类型,你应该专攻……通过一个子类。

    但是,您通常不需要以专门的方式做某事。您的 StringBar 示例有点做作,因为您可以这样做:

    public class Bar<T> {
         private final T value;
         public T get() {
            return value;
         }
    }
    

    我不明白你为什么需要在这里专门处理字符串。

    【讨论】:

    • 所以我唯一的选择是 StringBar 方法?
    • 基本上,get() 从其他地方检索 T 类型的实例,该实例未存储在类中,但如果出现错误,它返回 null,但我希望它返回 "" 为 String如果在字符串特化的情况下出现错误以防止对调用者进行额外的空值检查。
    • 顺便说一句,这个例子当然是人为的——那是我对你的怜悯,没有向你展示真正的来源,它很长很长,并且有损于真正的问题。真正的源代码有专门化的特定需求。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-18
    • 2023-04-05
    • 2013-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多