公共<T>无效运行(T 对象){} 是什么意思?



我正在阅读泛型,并尝试编写以下代码。没有编译错误。

import java.util.*;
public class JavaApplication14 {
    public<T>  void run (T obj ) {
        //Do Something
    }
  public static void main(String[] args) {     
      JavaApplication14  m= new JavaApplication14();
      m.run(new ArrayList<>());  
      m.run(new Interger(5);
      m.run(5);
     }
}

如果函数是

 public<T extends Number>  void run (T obj) {
            //Do Something
        }

这是有意义的,因为我们可以将这个函数的参数限制为Number及其子类型。但是我非常困惑的函数'运行'没有任何界限意味着什么?它现在可以接受任何对象作为参数吗?在什么情况下,我需要使用这样的泛型函数?

您的部分困惑可能源于这样一个事实,即在这种情况下将run作为通用方法是没有意义的。通常使用类型参数来创建两个参数类型之间和/或参数类型与返回类型之间的关系。在您的示例中,可以将run声明为需要Object参数(没有声明边界的类型参数有效地将Object作为其边界)。

我知道有一种情况可以在单个参数类型中使用类型参数:当您希望能够以一种不依赖于元素类型的方式操作集合,但需要向集合中插入元素时。例如,考虑一个假设的"反向列表"方法:

<T> void reverse(List<T> list)
{
    List<T> reversed = new ArrayList<T>();
    for (int i = list.size(); i > 0; i--) {
        reversed.add(list.get(i - 1));
    }
    list.clear();
    list.addAll(reversed);
}

用一种不需要类型参数的方式来写它是很困难的,也就是说,它需要一个List<?>参数。不使用强制类型转换的唯一方法是:

void reverse2(List<?> list)
{
    reverse(list);  // call reverse as defined above!
}

但同样,这并不适用于你讨论的例子。

总结一下:

没有显式绑定的类型参数实际上具有Object绑定。

方法可能需要类型参数(带或不带显式绑定)有两个原因:
  • 指定参数类型和/或返回类型之间的关系
  • 捕获潜在的通配符作为类型参数,以允许不可能的操作(如reverse示例)。

您讨论的示例方法:

public<T>  void run (T obj )

…两者都不做,所以类型参数是没有意义的。该方法也可以声明为public void run(Object obj)

它允许您避免任何强制转换

public class SomeClass {
      void doStuff();    
}
public<T extends SomeClass>  void run (T obj) {
    //can call doStuff without any casting
    obj.doStuff();
}
public<T>  void run (T) {
    //Here, there's no clue to perform the implicit cast.
    obj.doStuff();  //won't compile
}

虽然在这种情况下,函数也可以取Object,但对您有意义的变体也等同于public void run(Number obj) { ... }。对于缺少绑定的例子,考虑返回类型提到T: public <T> List<T> singletonList(T obj)的情况。

一些理论

有泛型方法。他们的主要目标是泛型算法(接收和返回相同的类型)。

使用泛型的代码比非泛型代码有很多好处:

  • 消除强制转换。
  • 在编译时进行更强的类型检查。
  • 允许程序员实现泛型算法。

小练习

考虑以下代码:

class MyClass {
    public void method() {}
    public static void main(String[] args) {
        runFirst(new MyClass());
        runSecond(new MyClass());
    }
    public static <T extends MyClass> void runFirst(T obj) {
        obj.method();
    }
    public static <T> void runSecond(T obj) {
        ((MyClass) obj).method();
    }
}

runFirst()方法允许我们避免强制转换为类及其所有子类。在runSecond()方法中,我们可以获得任何类型的参数(<T>,粗略地说,就是<T extends Object>)。首先,我们必须转换为MyClass,然后调用它的方法。

首先我将从public <T> void run(T object) { ... }的含义开始。是的,当你使用那种代码时,你可以使用任何对象作为run的参数。如果你想把这个函数的参数限制在一个特定的接口、类或它的子类中,你可以编写像NotGenericRun这样的代码,如下所示。

public class NotGenericRun {
    public void run(ArrayList<?> list) {
        String message = "Non Generic Run List: ";
        System.out.println(message.concat(list.toString()));
    }
    public void run(int intValue) {
        String message = "Non Generic Run Int: ";
        System.out.println(message.concat(String.valueOf(intValue)));
    }
}

这里我测试了GenericRunNotGenericRun类的输出。

public class TestClass {
    public static void main(String[] args) {
        GenericRun m = new GenericRun();
        m.run(new ArrayList<>());
        m.run(new Integer(5));
        m.run(5);
        NotGenericRun n = new NotGenericRun();
        n.run(new ArrayList<>());
        n.run(new Integer(5));
        n.run(13);
    }
}

该代码的输出如下:

Generic Run: []
Generic Run: 5
Generic Run: 5
Non Generic Run List: []
Non Generic Run Int: 5
Non Generic Run Int: 13

当你使用泛型运行时,正如我已经说过的,参数可以是任何对象,但还有其他方法可以在使用泛型的同时限制参数。

public class GenericRun {
    public <T> void run(T object) {
        String message = "Generic Run: ";
        System.out.println(message.concat(object.toString()));
    }
}

就是这样。

public class GenericRun <T> {
    public void run(T object) {
        String message = "Generic Run: ";
        System.out.println(message.concat(object.toString()));
    }
}

在本例中,您将使用GenericClass:

GenericRun<Integer> m = new GenericRun<Integer>();
m.run(new Integer(5));
m.run(5);

和它要处理的唯一值应该在创建类时声明。我想不出public <T> void run(T object) { ... }可能需要的场景,但当你需要方法来获取每个参数或者你不知道参数是什么时,可能会发生这种情况(但这真的不太可能)。我想更常见的情况是,当你像这样使用泛型时:

public class GenericRun <T> {
    public void run(T object) {
        ...
    }
}

我正在搜索泛型方法的用法,你可以在这里阅读更多关于为什么我们需要泛型方法的信息

下面是另一个例子:

public class GenericRun {
    public <T> void run(T[] inputArray) {
        for (T element : inputArray) {
            System.out.printf("%s ", element);
        }
        System.out.println();
    }
}

使用这个类,您可以使用单个泛型方法打印不同类型的数组:

public class TestClass {
    public static void main(String[] args) {
        GenericRun m = new GenericRun();
        // Create arrays of Integer, Double and Character
        Integer[] intArray = { 1, 2, 3, 4, 5 };
        Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
        Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
        System.out.println("Array integerArray contains:");
        m.run(intArray); // pass an Integer array
        System.out.println("nArray doubleArray contains:");
        m.run(doubleArray); // pass a Double array
        System.out.println("nArray characterArray contains:");
        m.run(charArray); // pass a Character array
    }
}

我希望我回答了你的问题。

唯一有意义的是,如果这是某种伪抽象或基类,它提供了行为框架,让其他编码人员实现自己的逻辑,但也提供默认的空操作。

它可以允许更好的通用类型设置,例如:

class MySubClass extends JavaApplication14 {
    public <T> void run(T obj){
        new ArrayList<T>().add(obj);
    }
}

最新更新