我正在阅读泛型并尝试编写以下代码。没有编译错误。
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及其子类型。但我非常困惑这个功能的运行&#39;没有任何约束意味着? 它现在可以将任何对象作为参数吗?在什么情况下我需要在泛型中使用这样的函数?
答案 0 :(得分:10)
你的一些困惑可能源于这样一个事实,即run
在这种情况下是一般的方法是没有意义的。通常使用类型参数来创建两个参数类型之间和/或参数类型和返回类型之间的关系。在您的示例中,run
也可以声明为需要Object
参数(没有声明绑定的类型参数实际上有Object
作为其绑定)。
我知道有一种情况可以在单个参数类型中有用地使用类型参数:当您希望能够以不依赖于元素类型的方式操作集合时,这需要将元素插入集合中。例如,考虑一个假设的反向列表&#34;方法:
<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)
。
答案 1 :(得分:3)
它允许您避免任何演员。
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
}
答案 2 :(得分:1)
虽然在这种情况下该函数也可以使用Object
,但对您有意义的变体也等同于public void run(Number obj) { ... }
。对于缺少绑定有意义的示例,请考虑返回类型提到T
:public <T> List<T> singletonList(T obj)
的情况。
答案 3 :(得分:1)
有通用的方法。他们的主要目标是通用算法(接收和返回相同的类型)。
使用泛型的代码比非通用代码有许多好处:
请考虑以下代码:
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
,然后调用它的方法。
答案 4 :(得分:1)
首先,我将从public <T> void run(T object) { ... }
的含义开始。当您使用那种代码时,可以使用任何对象作为运行参数。如果要将此函数的参数限制为特定接口,类或其子类,则可以编写如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
当你使用Generic run时,因为我已经说过,参数可能是任何对象,但还有其他方法在使用泛型时限制参数。
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) {
...
}
}
我正在搜索通用方法here的使用情况,您可以阅读更多有关我们为什么需要通用方法的信息。
这是另一个例子:
public class GenericRun {
public <T> void run(T[] inputArray) {
for (T element : inputArray) {
System.out.printf("%s ", element);
}
System.out.println();
}
}
使用此类,您可以使用单个Generic方法打印不同类型的数组:
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
}
}
我希望我回答你的问题。
答案 5 :(得分:0)
这里唯一有意义的是,如果这是某种伪抽象或基类,它为行为提供了框架,让另一个编码器实现自己的逻辑,但也提供了默认的空行动。
它可以允许更好的通用类型设置,例如:
class MySubClass extends JavaApplication14 {
public <T> void run(T obj){
new ArrayList<T>().add(obj);
}
}