可以实现 Java 泛型(模板)特化(使用特定类型覆盖模板类型)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3988180/
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-14 07:59:44  来源:igfitidea点击:

Java generics (template) specialization possible (overriding template types with specific types)

javagenerics

提问by Ash

I'm wondering what are the options to specialize generic types in Java, i.e. in a templated class to have specific overrides for certain types.

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

In my case I was a generic class (of type T) to return null usually, but return "" (the empty string), when T is the String type, or 0 (zero) when its the Integer type, etc.

在我的例子中,我是一个泛型类(T 类型),通常返回 null,但返回 ""(空字符串),当 T 是 String 类型时,或 0(零),当它是 Integer 类型时,等等。

Merely providing a type-specific overload of a method produces a "method is ambiguous" error:

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

e.g.:

例如:

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

Is the only option to subclass the generic class with a specific type (see StringBar in the following example?

是使用特定类型对泛型类进行子类化的唯一选择(请参阅以下示例中的 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 "";
    }
  }
}

Is this is the only way, it's a bit of a pain to have to create a subclass for every type I want to specialize instead of an overload of get() in the Bar class.

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

I'm guessing I could check the instanceof in the Bar.get() method, e.g. T get(T t) { if (t instanceof String) return ""; if (t instanceof Integer) return 0; else return null; }

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

However I've been taught to avoid instanceof and use polymorphism when possible.

然而,我被教导要避免使用 instanceof 并在可能的情况下使用多态。

采纳答案by Ash

All things considered, the concensus appears to be that the StringBar method method mentioned in the question is the only way to go.

考虑到所有因素,共识似乎是问题中提到的 StringBar 方法方法是唯一的方法。

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

回答by Mark Peters

Generics in Java aren't made for specialization. They're made for generalization! If you want to specialize for certain types, you should be specializing...through a subclass.

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

Often you don't need to do something in a specialized manner however. Your StringBar example is kind of contrived because you could have this:

但是,通常您不需要以专门的方式做某事。您的 StringBar 示例有点人为,因为您可以拥有以下内容:

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

I don't see why you need to specialize for a String here.

我不明白为什么你需要在这里专门研究一个字符串。

回答by Buhake Sindi

The compiler is actually correct, because the following code is compile-time checked (Bar<String> barString = new Bar<String>();) when compiled, from

编译器其实是正确的,因为下面的代码在编译的时候是编译时检查(Bar<String> barString = new Bar<String>();)的,从

public static class Bar<T> {

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

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

to

public static class Bar<String> {

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

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

and is ambiguous as you can't have 2 identical methods with the same return types and the same parameter arguments.

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

See an explanation by Jon Skeet's:

请参阅 Jon Skeet 的解释:



You can subclass Bar<T>and create StringBar(note I removed the statickeyword) and override get()method.

您可以Bar<T>创建子类并创建StringBar(注意我删除了static关键字)和覆盖get()方法。

public class BarString extends Bar<String> {

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

回答by DJClayworth

Generics in Java are very different from templates in C++ in this respect. It is not possible to write a specific version of a generic class to do something different for a particular case, as C++ can do. It is also not possible to determine at run time what T is - this is because that information is not passed into the byte code (object code) and so doesn't even exist at runtime. This due to something called "type erasure".

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

BarString and BarInt would be the obvious way of doing this, but there are improvements you can make. For example you can write a generic Bar to cover the common cases, and then write specialized BarString and BarInt to implement special cases. Ensure that the instances can only be created through a factory, which takes the classof the object to be processed:

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

That probably won't compile, but I don't have the time to work out the exact syntax. It does illustrate the principle.

那可能无法编译,但我没有时间计算出确切的语法。它确实说明了原理。