简体   繁体   English

Java Generics,支持“专业化”?与C ++模板的概念相似之处?

[英]Java Generics, support “Specialization”? Conceptual similarities to C++ Templates?

I know quite a bit how to use C++-Templates -- not an expert, mind you. 我知道如何使用C ++ - 模板 - 不是专家,请注意。 With Java Generics (and Scala, for that matter), I have my diffuculties. 使用Java Generics (和Scala,就此而言),我有我的困难。 Maybe, because I try to translate my C++ knowledge to the Java world. 也许,因为我试图将我的C ++知识转换为Java世界。 I read elsewhere, "they are nothing alike: Java Generics are only syntactic sugar saving casts, C++ Templates are only a glorified Preprocessor" :-) 我读到其他地方,“它们没什么相似之处:Java Generics只是语法糖节省演员,C ++模板只是一个美化的预处理器”:-)

I am quite sure, both is a bit simplified a view. 我很确定,两者都是一个简化的视图。 So, to understand the big and the subtle differences, I try to start with Specialization : 因此,为了理解大的和微妙的差异,我尝试从专业化开始:

In C++ I can design a Template (class of function) that acts on any type T that supports my required operations: C ++中,我可以设计一个模板 (函数类),它作用于支持我所需操作的任何类型T

template<typename T>
T plus(T a, T b) { return a.add(b); }

This now potentially adds the plus() operation to any type that can add() .[note1][1] 这现在可能会将plus()操作add()到任何可以add()类型。[note1] [1]

Thus, if T supports the add(T) my template woll work. 因此,如果T支持add(T)我的模板woll工作。 If it doesn't, The compiler will not complain as long as I do not use plus() . 如果没有,只要我不使用plus() ,编译器就不会抱怨。 In Python we call this "duck typing" : *If it acts like a duck, quacks like a duck, it is a duck.* (Of course, with using type_traits this is modified a bit, but as long as we have no concepts, this is how C++ Templates work, right?) 在Python中我们称之为“鸭子打字” :*如果它像鸭子一样嘎嘎叫,就像鸭子一样,它一只鸭子。*(当然,使用type_traits这有点修改,但只要我们没有概念,这就是C ++模板的工作方式,对吧?)

I guess, thats how Generics in Java work as well, isn't it? 我想,那就是Java中的泛型如何工作,不是吗? The generic type I device is used as a "template" how to operate on any anything I try to put in there, right? 通用类型I设备用作“模板”如何操作我试图放在那里的任何东西,对吧? As far as I understand I can (or must?) put some constraints on the type arguments: If I want to use add in my template, I have to declare the type argument to implement Addable . 据我所知,我可以(或必须?)对类型参数设置一些约束 :如果我想在模板中使用add ,我必须声明type参数来implement Addable Correct? 正确? So, no "duck typing" (for better or worse). 所以,没有“鸭子打字”(无论好坏)。

Now, in C++ I can choose to specialize on a type that has no add() : 现在,在C ++中,我可以选择专注于没有add()的类型:

template<>
T plus<MyX>(MyX a, MyX b) { return a + b; }

And even if all other types still can use the "default" implementation, now I added a special one for MyX -- with no runtime overhead. 即使所有其他类型仍然可以使用“默认”实现,现在我为MyX添加了一个特殊的 - 没有运行时开销。

Is there any Java Generics mechanism that has the same purpose? 是否有任何Java Generics机制具有相同的目的? Of course, in programming everything is doable, but I mean conceptually , without any tricks and magic? 当然,在编程中,一切都是可行的,但我的意思是概念上 ,没有任何技巧和魔力?

No, generics in Java don't work this way. 不,Java中的泛型不能以这种方式工作。

With generics you can't do anything which would not be possible without Generics - you just avoid to have to write lots of casts, and the compiler ensures that everything is typesafe (as long as you don't get some warnings or suppress those). 使用泛型,如果没有泛型,你就无法做任何事情 - 你只需要避免编写大量的强制转换,并且编译器确保一切都是类型安全的(只要你没有得到一些警告或抑制那些) 。

So, for each type variable you can only call the methods defined in its bounds (no duck typing). 因此,对于每个类型变量,您只能调用其边界中定义的方法(没有鸭子类型)。

Also, there is no code generation (apart from some adapter methods to delegate to methods with other parameter types for the purpose of implementing generic types). 此外,没有代码生成(除了为了实现泛型类型而委托给具有其他参数类型的方法的一些适配器方法)。 Assume you had something like this 假设你有类似的东西

/**
 * interface for objects who allow adding some other objects
 */
interface Addable<T> {
   /** returns the sum of this object and another object. */
   T plus(T summand);
}

Then we could create our sum method with two arguments: 然后我们可以使用两个参数创建sum方法:

public static <T extends Addable<T>> T sum(T first, T second) {
    return first.plus(second);
}

The static method is compiled to the same bytecode like this (with additional type information in annotations): 静态方法被编译为相同的字节码(注释中包含其他类型信息):

public static Addable sum(Addable first, Addable second) {
    return first.plus(second);
}

This is called type erasure . 这称为类型擦除

Now this method can be called for every pair of two elements of an addable type, like this one: 现在可以为可添加类型的每两对元素调用此方法,如下所示:

public class Integer implements Addable<Integer> {
    public Integer plus(Integer that) {
       return new Integer(this.value + that.value);
    }

     // private implementation details omitted
}

What here happens is that the compiler creates an additional synthetic method like this: 这里发生的是编译器创建一个额外的合成方法,如下所示:

public Object plus(Object that) {
    return this.plus((Integer)that);
}

This method will only be called by generic code with the right types, this guarantees the compiler, assuming you are not doing some unsafe casts somewhere - then the (Integer) cast here will catch the mistake (and throw a ClassCastException). 这个方法只能由具有正确类型的通用代码调用,这保证了编译器,假设你没有在某处做一些不安全的转换 - 那么这里的(Integer)转换将捕获错误(并抛出一个ClassCastException)。

The sum method now always calls the plus method of the first object, there is no way around this. sum方法现在总是调用第一个对象的plus方法,没有办法解决这个问题。 There is not code generated for every type argument possible (this is the key difference between Java generics and C++ templates), so we can't simply replace one of the generated method with a specialized one. 没有为每种类型参数生成代码(这 Java泛型和C ++模板之间的关键区别),因此我们不能简单地用专门的方法替换其中一个生成的方法。

Of course, you can create a second sum method like irreputable proposed (with overloading), but this will only be selected if you use the MyX type directly in source code, not when you are calling the sum method from some other generic code which happens to be parametrized with MyX, like this: 当然,您可以创建第二个sum方法,如无可争议的建议(带有重载),但只有在源代码中直接使用MyX类型时才会选择此方法,而不是在从其他一些通用代码调用sum方法时用MyX进行参数化,如下所示:

public static <T extends Addable<T>> product (int times, T factor) {
    T result = factor;
    while(n > 1) {
        result = sum(result, factor);
    }
    return result;
}

Now product(5, new MyX(...)) will call our sum(T,T) method (which in turn calls the plus method), not any overloaded sum(MyX, MyX) method. 现在product(5, new MyX(...))将调用我们的sum(T,T)方法(进而调用plus方法),而不是任何重载sum(MyX, MyX)方法。

(JDK 7 adds a new dynamic method dispatch mode which allows specialization by every argument on run time, but this is not used by the Java language, only intended to be used by other JVM-based languages.) (JDK 7添加了一种新的dynamic方法调度模式,它允许运行时每个参数进行特化,但Java语言不使用它,仅用于其他基于JVM的语言。)

no - but your particular problem is more of an overloading issue. 不 - 但你的特殊问题更多的是超载问题。

There's no problem to define 2 plus methods like these 定义2 plus方法没有问题

<T extends Addable> 
T   plus(T   a, T   b) { .. }

MyX plus(MyX a, MyX b) { .. }

This works even if MyX is an Addable ; 即使MyXAddable ,这也能正常工作; javac knows that the 2nd plus is more specific than the 1st plus , so when you call plus with two MyX args, the 2nd plus is chosen. javac知道第二个plus比第一个plus号更具体,所以当你用两个MyX args调用plus时,选择第二个plus In a sense Java does allow "specialized" version of methods: 从某种意义上说,Java确实允许“专用”版本的方法:

f(T1, T2, .. Tn)

f(S1, S2, .. Sn)

works great if each Si is a subtype of Ti 如果每个SiTi的子类型,则效果很好

For generic classes, we can do 对于泛型类,我们可以做到

class C<T extends Number> { ... }

class C_Integer extends C<Integer>{ ... } 

caller must use C_Integer instead of C<Integer> to pick the "specialized" version. 调用者必须使用C_Integer而不是C<Integer>来选择“专用”版本。


On duck typing: Java is more strict in static typing - unless it is a Duck, it is not a duck. 关于鸭子打字:Java在静态打字中更严格 - 除非它是鸭子,它不是鸭子。

HI, HI,

java Generics it's different from C++ template. java Generics它与C ++模板不同。

Example: 例:

Java code: Java代码:

 public <T> T sum(T a, T b) {
  T newValue = a.sum(b);
  return newValue;
 }

In java this code don't work because generics base is class java.lang.Object, so you can use only method of this class. 在java中,此代码不起作用,因为泛型基类是类java.lang.Object,因此您只能使用此类的方法。

you can construct this methis like this: 你可以像这样构建这个methis:

public <T extends Number> T sum(T a, T b) {
  T newValue = a.sum(b);
  return newValue;
 } 

in this case the base of generics is class java.lang.Number so you can use Integer, Double, Long ecc.. 在这种情况下,泛型的基础是java.lang.Number类,所以你可以使用Integer,Double,Long ecc ..

method "sum" depend of implementation of java.lang.Number. 方法“sum”取决于java.lang.Number的实现。

Bye 再见

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM