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

发布于 2024-10-29 05:39:53 字数 1341 浏览 2 评论 0原文

我知道很多如何使用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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

过期以后 2024-11-05 05:39:53

不,Java 中的泛型不是这样工作的。

使用泛型,你不能做任何没有泛型就不可能做的事情 - 你只是避免编写大量的强制转换,并且编译器确保一切都是类型安全的(只要你没有收到一些警告或抑制这些警告) 。

因此,对于每个类型变量,您只能调用其边界中定义的方法(无鸭子类型)。

此外,没有代码生成(除了一些适配器方法为了实现泛型类型而委托给具有其他参数类型的方法)。假设你有这样的东西

/**
 * 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);
}

然后我们可以创建带有两个参数的 sum 方法:

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

静态方法被编译为相同的字节码,如下所示(在注释中带有附加类型信息):

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

这称为 类型擦除

现在,可以为可添加类型的每对两个元素调用此方法,如下所示:

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

     // private implementation details omitted
}

这里发生的情况是编译器创建一个额外的合成方法,如下所示:

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

该方法只能由具有正确类型的泛型代码调用,这可以保证编译器,假设您没有在某处进行一些不安全的转换 - 那么这里的 (Integer) 转换将捕获错误(并抛出 ClassCastException)。

sum 方法现在总是调用第一个对象的 plus 方法,没有办法解决这个问题。不会为每种可能的类型参数生成代码(这是 Java 泛型和 C++ 模板之间的主要区别),因此我们不能简单地将生成的方法之一替换为专门的方法。

当然,您可以创建第二个 sum 方法,例如 irreputable 提出的(带有重载),但只有在使用 MyX 时才会选择此方法直接在源代码中键入,而不是当您从其他恰好使用 MyX 参数化的通用代码调用 sum 方法时,如下所示:

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

现在 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

/**
 * 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:

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).

The sum method now always calls the plus 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 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:

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.

(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.)

一张白纸 2024-11-05 05:39:53

不 - 但你的具体问题更多的是一个超载问题。

定义 2 个这样的 plus 方法是没有问题的

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

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

,即使 MyX 是一个 Addable 也是如此; javac 知道第二个 plus 比第一个 plus 更具体,因此当您使用两个 MyX 调用 plus 时args,选择第二个plus。从某种意义上说,Java 确实允许方法的“专用”版本:

f(T1, T2, .. Tn)

f(S1, S2, .. Sn)

如果每个 Si 都是 Ti 的子类型,那么效果很好

对于泛型类,我们可以

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

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

调用者必须使用 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 these

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

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

This works even if MyX is an Addable; 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. In a sense Java does allow "specialized" version of methods:

f(T1, T2, .. Tn)

f(S1, S2, .. Sn)

works great if each Si is a subtype of Ti

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.


On duck typing: Java is more strict in static typing - unless it is a Duck, it is not a duck.

爱你不解释 2024-11-05 05:39:53

你好,

java 泛型它与 C++ 模板不同。

示例:

Java 代码:

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

在 java 中,此代码不起作用,因为泛型基类是 java.lang.Object 类,因此您只能使用此类的方法。

你可以这样构造这个方法:

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

在这种情况下,泛型的基础是类 java.lang.Number,因此你可以使用 Integer、Double、Long ecc..

方法“sum”取决于 java.lang.Number 的实现。

再见

HI,

java Generics it's different from C++ template.

Example:

Java code:

 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.

you can construct this methis like this:

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..

method "sum" depend of implementation of java.lang.Number.

Bye

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文