Java泛型,支持“专业化”吗?与 C++ 的概念相似之处模板?
我知道很多如何使用C++-Templates——请注意,我不是专家。对于 Java 泛型(以及 Scala,就此而言),我遇到了困难。也许吧,因为我试图将我的 C++ 知识转化为 Java 世界。我在其他地方读到,“它们毫无相似之处:Java 泛型只是节省语法糖的转换,C++ 模板只是一个美化的预处理器”:-)
我很确定,两者都是有点简化的视图。因此,为了理解大的和细微的差异,我尝试从专业化开始:
在C++中,我可以设计一个模板(函数类) ) 作用于支持我所需操作的任何类型T
:
template<typename T>
T plus(T a, T b) { return a.add(b); }
现在可能会将plus()
操作添加到任何可以>add()
.[note1][1]
因此,如果 T
支持 add(T)
我的模板就可以工作。如果没有, 只要我不使用plus()
,编译器就不会抱怨。在Python中 我们称之为“鸭子打字”:*如果它像鸭子一样行动,像鸭子一样嘎嘎叫, 它是一只鸭子。*(当然,使用 type_traits 会对此进行一些修改, 但只要我们没有概念,这就是 C++ 模板的工作原理,对吧?)
我想,这也是Java 中的泛型的工作原理,不是吗?通用 I 型设备用作“模板”,如何对我尝试放入其中的任何内容进行操作,对吧?据我了解,我可以(或必须?)对类型参数添加一些约束:如果我想在模板中使用add
,我必须声明类型实现 Addable
的参数。正确的?所以,没有“鸭子打字”(无论好坏)。
现在,在C++中,我可以选择专门化没有add()
的类型:
template<>
T plus<MyX>(MyX a, MyX b) { return a + b; }
即使所有其他 类型仍然可以使用“默认”实现,现在我为 MyX
添加了一个特殊的实现——没有运行时开销。
是否有任何具有相同目的的Java泛型机制?当然,在编程中一切都是可行的,但我的意思是概念上,没有任何技巧和魔法?
I know quite a bit how to use C++-Templates -- not an expert, mind you. With Java Generics (and Scala, for that matter), I have my diffuculties. Maybe, because I try to translate my C++ knowledge to the Java world. I read elsewhere, "they are nothing alike: Java Generics are only syntactic sugar saving casts, C++ Templates are only a glorified Preprocessor" :-)
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:
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]
Thus, if T
supports the add(T)
my template woll work. If it doesn't,
The compiler will not complain as long as I do not use 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?)
I guess, thats how Generics in Java work as well, isn't it? The generic type I device is used as a "template" how to operate on any anything I try to put in there, right? 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
. Correct? So, no "duck typing" (for better or worse).
Now, in C++ I can choose to specialize on a type that has no 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.
Is there any Java Generics mechanism that has the same purpose? Of course, in programming everything is doable, but I mean conceptually, without any tricks and magic?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
不,Java 中的泛型不是这样工作的。
使用泛型,你不能做任何没有泛型就不可能做的事情 - 你只是避免编写大量的强制转换,并且编译器确保一切都是类型安全的(只要你没有收到一些警告或抑制这些警告) 。
因此,对于每个类型变量,您只能调用其边界中定义的方法(无鸭子类型)。
此外,没有代码生成(除了一些适配器方法为了实现泛型类型而委托给具有其他参数类型的方法)。假设你有这样的东西
然后我们可以创建带有两个参数的
sum
方法:静态方法被编译为相同的字节码,如下所示(在注释中带有附加类型信息):
这称为 类型擦除。
现在,可以为可添加类型的每对两个元素调用此方法,如下所示:
这里发生的情况是编译器创建一个额外的合成方法,如下所示:
该方法只能由具有正确类型的泛型代码调用,这可以保证编译器,假设您没有在某处进行一些不安全的转换 - 那么这里的
(Integer)
转换将捕获错误(并抛出 ClassCastException)。sum
方法现在总是调用第一个对象的plus
方法,没有办法解决这个问题。不会为每种可能的类型参数生成代码(这是 Java 泛型和 C++ 模板之间的主要区别),因此我们不能简单地将生成的方法之一替换为专门的方法。当然,您可以创建第二个
sum
方法,例如 irreputable 提出的(带有重载),但只有在使用MyX
时才会选择此方法直接在源代码中键入,而不是当您从其他恰好使用 MyX 参数化的通用代码调用sum
方法时,如下所示:现在
product(5, new MyX(.. .))
将调用我们的sum(T,T)
方法(该方法又调用plus
方法),而不是任何重载的sum(MyX , MyX)
方法。(JDK 7 添加了一个新的动态方法分派模式,允许在运行时对每个参数进行专门化,但这不是 Java 语言使用的,仅适用于其他基于 JVM 的语言。)
No, generics in Java don't work this way.
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
Then we could create our
sum
method with two arguments:The static method is compiled to the same bytecode like this (with additional type information in annotations):
This is called type erasure.
Now this method can be called for every pair of two elements of an addable type, like this one:
What here happens is that the compiler creates an additional synthetic method like this:
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).The
sum
method now always calls theplus
method of the first object, there is no way around this. 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.Of course, you can create a second
sum
method like irreputable proposed (with overloading), but this will only be selected if you use theMyX
type directly in source code, not when you are calling thesum
method from some other generic code which happens to be parametrized with MyX, like this:Now
product(5, new MyX(...))
will call oursum(T,T)
method (which in turn calls theplus
method), not any overloadedsum(MyX, MyX)
method.(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.)不 - 但你的具体问题更多的是一个超载问题。
定义 2 个这样的
plus
方法是没有问题的,即使
MyX
是一个Addable
也是如此; javac 知道第二个plus
比第一个plus
更具体,因此当您使用两个MyX
调用plus
时args,选择第二个plus
。从某种意义上说,Java 确实允许方法的“专用”版本:如果每个
Si
都是Ti
的子类型,那么效果很好对于泛型类,我们可以
调用者必须使用
C_Integer
而不是C
来选择“专用”版本。关于鸭子类型:Java 在静态类型方面更加严格 - 除非它是鸭子,否则它不是鸭子。
no - but your particular problem is more of an overloading issue.
There's no problem to define 2
plus
methods like theseThis works even if
MyX
is anAddable
; javac knows that the 2ndplus
is more specific than the 1stplus
, so when you callplus
with twoMyX
args, the 2ndplus
is chosen. In a sense Java does allow "specialized" version of methods:works great if each
Si
is a subtype ofTi
For generic classes, we can do
caller must use
C_Integer
instead ofC<Integer>
to pick the "specialized" version.On duck typing: Java is more strict in static typing - unless it is a Duck, it is not a duck.
你好,
java 泛型它与 C++ 模板不同。
示例:
Java 代码:
在 java 中,此代码不起作用,因为泛型基类是 java.lang.Object 类,因此您只能使用此类的方法。
你可以这样构造这个方法:
在这种情况下,泛型的基础是类 java.lang.Number,因此你可以使用 Integer、Double、Long ecc..
方法“sum”取决于 java.lang.Number 的实现。
再见
HI,
java Generics it's different from C++ template.
Example:
Java code:
In java this code don't work because generics base is class java.lang.Object, so you can use only method of this class.
you can construct this methis like this:
in this case the base of generics is class java.lang.Number so you can use Integer, Double, Long ecc..
method "sum" depend of implementation of java.lang.Number.
Bye