我正在阅读泛型,并尝试编写以下代码。没有编译错误。
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)));
}
}
这里我测试了GenericRun
和NotGenericRun
类的输出。
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);
}
}