【问题标题】:Why Generics in Java? How it is different from overloading?为什么在 Java 中使用泛型?它与重载有何不同?
【发布时间】:2011-06-14 12:37:56
【问题描述】:

我想在 Java 中编写一个方法 add(),它可以同时添加整数、字符串等。Is Generics 会帮助我。

我无法理解泛型的最终目的。我很困惑。

泛型与重载?

public Integer add(Integer i, Integer j){return i+j;}
public String add(String i, String j){return i+j;}
public <T> T add(T i, T j){return i+j;} //this gives me error.

请让我摆脱它。

谢谢。

【问题讨论】:

  • 当您添加Integer i + Integer j 时,您实际上并没有添加IntegersIntegers 不支持 + 运算符。它们被拆箱到ints,而不是直接添加为Integer 对象。您不能添加通用对象,因为对象不支持 + 运算符。 Strings 是一个特例,支持+
  • 您将两个示例都称为add 的唯一原因是因为它们使用+ 运算符,这实际上是一种毫无意义的相似性,因为它对字符串和整数做了完全不同的事情。从调用者的角度来看,根据它们实际所做的来命名方法 - 在这种情况下,sumconcatenate 将是好的名称。

标签: java generics


【解决方案1】:

泛型可以帮助,这里的问题是+ 操作只为java 原语和String 定义,而不是一般类型。在 Java 中,我们不能重载运算符(例如,我们可以在 C++ 中重载)。

没有泛型的实用解决方案是:

public Integer add(Integer a, Integer b) { return a + b; }    // sum of a and b
public String add(String a, String b) { return a + b; }      // concatenate operation!
public MyType add(MyType a, MyType b) { return a.add(b); }   // requires add operation

【讨论】:

  • 你的第二段不是真的。返回类型不是 Java 中方法签名的一部分,只是名称和参数类型。只要参数不同,就可以有不同的返回类型。还是我误解了你写的内容?
  • 重载不是由返回类型决定的。重载的返回类型不必相同。
  • @Jonathan - 非常感谢 - 这是一个典型的 ShortOnCoffeeError :-) 更改了答案并预订了另一门“Java 初学者”课程;)
  • @Jonathan:返回类型是签名的一部分,但你不能有两种仅在返回类型上有所不同的方法(因为那样你调用的方法太模棱两可了)
【解决方案2】:

其他人指出了为什么这不起作用。让我补充一下,我认为您误解了 Java 泛型的用途。也许您熟悉 C++ 模板,或者 C 宏扩展,它们都类似于泛型,但又是完全不同的东西。

泛型实际上是关于类型安全和避免混乱的强制转换。您告诉编译器某个集合包含字符串,然后它知道您放入或取出的所有内容都必须是字符串。您可以在编译时检查您放入的内容,并为您取出的内容节省一些演员表。

它不会导致编译器根据类型生成不同的代码。泛型不是避免编写同一函数的“int”版本和“float”版本的捷径,就像宏或 C++ 模板会为您做的那样。您可以通过让两个类实现相同功能但一个使用 int 而另一个使用浮点数来获得这种所需的不同行为。但是您可以使用没有泛型的面向对象技术来做到这一点。如果您无法使用“非泛型”面向对象技术获得所需的独特行为,那么您也无法使用泛型来实现。

【讨论】:

    【解决方案3】:

    我想在 Java 中编写一个 add() 方法,它可以同时添加整数、字符串等。Is Generics 会帮助我。

    泛型主要在编译时用于类型安全

    您只能指定一种类型添加到集合中

    List&lt;String&gt; lst;

    只接受String,没有String的超类没有子类[例如如果存在]

    【讨论】:

    • will accept String only, no superclass of String no sub class - 这不是真的。像void add42(List&lt;Number&gt; list) {list.add(42);} 这样的东西是完全有效的。
    【解决方案4】:
    public Integer add(Integer i, Integer j){return i+j;}
    public String add(String i, String j){return i+j;}
    public <T> T add(T i, T j){return i+j;} //this gives me error.
    

    虽然这些方法看起来几乎相同,但在这种情况下,泛型无法提供帮助。考虑一下:

    public <T> T add(T i, T j){return i+j;}
    ...
    add(new Object(), new Object());
    

    把普通的Objects 加在一起是什么意思?不,Object 不支持+,并且许多其他可以替代T 的类型也不支持。

    在创建泛型方法&lt;T&gt; T add(T i, T j) 时,泛型方法仅限于泛型类型参数T 上允许的方法和操作,这实际上意味着您可以使用Object 执行的操作。您正在尝试使用 + 的方法/操作,这是您可以使用 Object 执行的操作。

    我们可以尝试通过找到一个公共基类StringInteger 来解决这个问题,它支持我们在泛型方法体中必须使用的所有方法和操作。 如果有这样一个公共基类XXXXX 支持+ 运算符,我们可以这样做:

    public <T extends XXXXX> T add(T i, T j) { return i+j; }
    

    但是,没有通用基类XXXXX支持我们在泛型方法(+)中想做的一切,所以泛型不能用来解决这个问题。

    【讨论】:

    • 感谢您提及extends,我正打算自己这样做。
    【解决方案5】:

    泛型很好,因为如果处理得当,它可以让您概括您编写的一些代码。在编写软件方面,您希望尽量减少不必要的代码并尽可能多地重用代码。

    通过使用重载方法,您必须写出每个重载,而使用泛型您只需执行一次。泛型在您操作集合的情况下工作得很好,因为您对一组集合执行某些常见操作。在某些情况下,如果您希望使用更定制的方法来处理某些情况,那么您可能必须重载方法,但如果您可以在多个地方使用泛型,我会以编写泛型为目标。

    【讨论】:

    • 所以我将 Java 泛型与 C++ 模板混淆了。如果任何用于比较泛型和模板的链接对我更有帮助。
    • stackoverflow.com/questions/36347/…可以帮助你了解这两者的异同。
    【解决方案6】:

    这正是泛型的意义所在。 您不想实现一种可以接受所有内容的方法。坦率地说,你总是可以实现方法

    add(Object obj)

    并将整数、字符串、布尔值传递给它...但在大多数情况下,您不想这样做。该方法必须处理参数,因此它应该知道参数的类型。这就是如果你想让接收字符串和整数的方法 add 实现 2 个方法的原因:

    add(String s);
    add(int i);
    

    现在您不能将布尔值发送到 add() 方法:这种方法根本不存在。

    你也可以实现泛型方法

    &lt;T&gt; void add(T arg);

    并将其命名为:this.&lt;String&gt;add("hello");this.&lt;Integer&gt;add(123);

    在这种情况下,this.&lt;String&gt;add(123); 会导致编译错误。

    【讨论】:

      【解决方案7】:

      Java 中的泛型只向字节码添加强制转换。

      代码将是相同的,除了强制转换。

      所以这两种结构:

      List list1 = new ArrayList();
      List<String list2 = new ArrayList<String>();
      ....
      String string1 = (String) list1.get(0);
      String string2 = list2.get(0);
      

      正在做同样的事情。

      【讨论】:

        【解决方案8】:

        使用通用接口的方法怎么样:

        interface Adder<T> {
            T add(T first, T second);
        }
        

        还有一个经理类:

        public class AdderManager{
        
            public <T> AdderManager putAdder(final Class<T> clazz,
                final Adder<? extends T> adder){
                adders.put(clazz, adder);
                return this;
            }
        
            @SuppressWarnings("unchecked")
            public <T> Adder<T> getAdder(final Class<T> clazz){
                return (Adder<T>) adders.get(clazz);
            }
        
            private final Map<Class<?>, Adder<?>> adders =
                new HashMap<Class<?>, Adder<?>>();
        
        }
        

        现在您可以为不同的类注册和使用自定义加法器:

        final AdderManager manager = new AdderManager();
        manager
        .putAdder(String.class, new Adder<String>(){
        
            @Override
            public String add(final String first, final String second){
                return first.concat(second);
            }
        })
        .putAdder(Integer.class, new Adder<Integer>(){
        
            @Override
            public Integer add(final Integer first, final Integer second){
                return first + second;
            }
        })
        .putAdder(List.class, new Adder<List<?>>(){
        
            @Override
            public List<?> add(final List<?> first, final List<?> second){
                final List<Object> newList = new ArrayList<Object>();
        
                return newList;
                }
        });
        

        现在你可以像这样使用这些加法器了:

        String addedString = manager.getAdder(String.class).add("abc", "def");
        
        @SuppressWarnings("unchecked") // this is necessary because of
                                       // the generic <Integer> type
        List<Integer> addedList = manager
                                    .getAdder(List.class)
                                    .add(
                                        Arrays.asList(1,2,3),
                                        Arrays.asList(4,5,6)
                                     );
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-03-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-09
          • 1970-01-01
          相关资源
          最近更新 更多