java泛型
来源:互联网 发布:三国杀网络错误请重试 编辑:程序博客网 时间:2024/05/01 22:06
泛型是JAVA SE5之后才出现的概念,“泛型”其实就是指类型的参数化。我们知道Java是单继承体系的,这就使得我们在编写程序时,很容易受到限制,不能实现代码的复用。泛型一个重要的应用就是实现了“容器类”,如果不使用泛型,我们只能编写下面的程序:
Java代码
public class Holder {
private Object a;
public Holder(Object a) { this.a = a; }
public void set(Object a) { this.a = a; }
public Object get() { return a; }
public static void main(String[] args) {
Holder h2 = new Holder("The String");
String s = (String)h2.get();
h2.set(1);
Integer x = (Integer)h2.get();
}
}
虽然我们可以实现了可以同时持有不同的类型,但是我们取出时必须进行强制类型转化,如果不小心还容易产生CLASSCASTEXCEPTION,如果我们使用泛型的话,可以采用下面的实现方式。由上海java培训机构推荐阅读
Java代码
class Automobile{}
public class GenericHolder<T> {
private T a;
public GenericHolder(T a) { this.a = a; }
public void set(T a) { this.a = a; }
public T get() { return a; }
public static void main(String[] args) {
GenericHolder<Automobile> h3 =
new GenericHolder<Automobile>(new Automobile());
Automobile a = h3.get(); // No cast needed
// h3.set("The String"); // Error
// h3.set(1); // Error
}
}
我们通过将想要持有的对象预先放在尖括号内,编译器就会对放置的类型进行检测,防止出错。泛型应用于接口与泛型类差不多,这里就不在解释了。
泛型也可以应用于方法,泛型方法使得方法能够独立于类产生变化。泛型方法与其所处的类是否为泛型是没有任何关系的。
Java代码
public class GenericMethods {
public <T> void f(T x) {
System.out.println(x.getClass().getName());
}
public static void main(String[] args) {
GenericMethods gm = new GenericMethods();
gm.f("");
gm.f(1);
gm.f(1.0);
gm.f(1.0F);
gm.f('c');
gm.f(gm);
}
}
对比以上的泛型方法与泛型类,可以看出:当使用泛型类是,必须在创建对象的时候指定类型参数的值,而使用泛型方法时,通常不用指明参数的类型;编译器可以通过类型推断为我们找出具体的类型(只在赋值操作时起作用)。其他的时候也需要显示的指明类型,此时可以在点操作符与方法名之间插入尖括号例如:
Java代码
gm.<GenericMethods>f(gm);
可以看出泛型方法较泛型类更加的简洁和容易理解,所以我们在编程时应当尽量的使用泛型方法。
以上为泛型的基础应用,在学习泛型时,看到下面一处奇怪的代码:
Java代码
public class ErasedTypeEquivalence {
@SuppressWarnings("rawtypes")
public static void main(String[] args) {
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2);
}
}
对于ArrayList<String>和ArrayList<Integer>我们很容易认为它们是不同的类型,的确它们是不同的类型然而程序的输出却是"TRUE";这是由于Java的泛型是使用擦出来实现的,当我们使用泛型时,任何具体的类型信息都被擦除了,List<String>和List<Integer>在运行时都被擦出为成原生类型List了。
然而Java为什么要这么做呢?我们前面提到泛型在JAVA SE5之后才出现的,Java泛型使用擦除的原因在于向前兼容。即即使类库进行了代码改造使用了新的泛型实现,客户端依然可以在不改变代码的前提下继续使用这个类库,使得在不破坏现有类库的前提下,将泛型融入到了java语言中。更多Java相关请移步http://www.shaccp.cn/
然而通过擦除实现了Java中的泛型,必然会付出一定的代价,使得Java中的泛型不能用于显示地引用运行时类型的操作之中,例如:转型、instanceof操作和new表达式,而这些在C++中是可以使用的,在java中我们可以通过反射来间接的实现。由于擦出的存在下面这种方式试图重载方法是不正确的:
Java代码
public class UseList<W, T> {
//错误:Method f(List<T>) has the same erasure f(List<E>) as another method in type UseList<W,T>
void f(List<T> v){}
void f(List<W> v){}
}
所以我们应当注意的是java中的泛型只有在静态类型检查期间才会出现,在此之后,程序中所有的泛型类型都将被擦出,替换为它的非泛型边界。
什么边界?
边界使得我们可以在用于泛型的参数类型上设置限制条件:一方面使得我们可以规定泛型参数可以使用的类型,另一方面使得我们可以按照自己的边界类型来调用方法。例如:
Java代码
class HasF {
public void f() {
System.out.println("HasF.f()");
}
}
class Manipulator<T> {
private T obj;
public Manipulator(T x) {
obj = x;
}
// Error: 未发现方法f()
public void manipulate() {
obj.f();
}
}
public class Manipulation {
public static void main(String[] args) {
HasF hf = new HasF();
Manipulator<HasF> manipulator = new Manipulator<HasF>(hf);
manipulator.manipulate();
}
}
由于使用了擦除,Java编译器不能obj可以调用方法f()映射到HasF拥有方法f()这个情况上,此时我们可以使用边界,例如:
Java代码
class Manipulator2<T extends HasF> {
private T obj;
public Manipulator2(T x) {
obj = x;
}
public void manipulate() {
obj.f();
}
}
此时obj可以直接调用方法f()了。然后我们可以看出此时这样的泛型并没有给我们任何好处,我们不使用泛型,只需将Manipulator<T>中的T替换为HasF并且使用原生类Manipulator也可以实现以上的功能。所以使用泛型时我们最好自己的思考一下,我们的问题是否足够的复杂到必须使用泛型的程度,也就是说我们编写的这部分代码能够跨多个类进行工作。
我们看到
Java代码
class Manipulator2<T extends HasF>
这里的extends与我们在普通情况下看到的是不同的,因为java泛型重用了extends关键字。
我们继续分析下面一个比较难以理解的例子:
Java代码
class Fruit {}
class Apple extends Fruit {}
class Jonathan extends Apple {}
class Orange extends Fruit {}
public class GenericsAndCovariance {
public static void main(String[] args) {
//编译错误:Type mismatch: cannot convert from ArrayList<Apple> to List<Fruit>
//List<Fruit> flist = new ArrayList<Apple>();
List<? extends Fruit> flist = new ArrayList<Apple>();
// 编译错误:不能添加任何类型
//flist.add(new Apple());
// flist.add(new Fruit());
// flist.add(new Object());
flist.add(null); // Legal but uninteresting
Fruit f = flist.get(0);
List<Fruit> flist2 = new ArrayList<>();
flist2.add(new Apple());
}
}
如下:List<Fruit>表示持有Fruit以及Fruit的子类型,我们可以向flist2中添加Fruit的子类型Apple,然而Apple的List在类型上并不等价于Fruit的List
Java代码
List<Fruit> flist = new ArrayList<Apple>();
再看flist类型为List<? extends Fruit>我们可以理解为“具有任何从Fruit继承的某种类型的列表” 通配符表示flist指向某种没有指定的具体类型,听着有些拗口,这里?指某种而不是某些。如果允许以下操作:
Java代码
flist.add(new Apple());
flist.add(new Fruit());
flist.add(new Object());
我们在取出时由于不知道flist中的具体类型,又必须进行强制类型转换,这与泛型的思想又是背道而驰的。
所以我们在使用<? extends SomeClass>时,意味着我们只能通过这个名称来获取或者移除某些信息,而不能增加它的信息。
如果想要写入或者增加信息我们可以使用超类行通配符<? super SomeClass>,例如:
Java代码
public class SuperTypeWildcards {
static void writeTo(List<? super Apple> apples) {
apples.add(new Apple());
apples.add(new Jonathan());
// apples.add(new Fruit()); // Error
}
}
泛型中还存在另一种无界通配符<?>,意味着任何事物”
Java代码
public class UnboundedWildcards1 {
@SuppressWarnings("rawtypes")
static List list1;
static List<?> list2;
static List<? extends Object> list3;
static void assign1(@SuppressWarnings("rawtypes") List list) {
list1 = list;
list2 = list;
list3 = list; // Warning: unchecked conversion
// Found: List, Required: List<? extends Object>
}
static void assign2(List<?> list) {
list1 = list;
list2 = list;
list3 = list;
}
static void assign3(List<? extends Object> list) {
list1 = list;
list2 = list;
list3 = list;
}
@SuppressWarnings("rawtypes")
public static void main(String[] args) {
list1.add(new Integer(1));
// 错误list2与list3中只能添加null
// list2.add(new Integer(1));
// list3.add(new Integer(1));
assign1(new ArrayList());
assign2(new ArrayList());
assign3(new ArrayList()); // Warning:
// Unchecked conversion. Found: ArrayList
// Required: List<? extends Object>
assign1(new ArrayList<String>());
assign2(new ArrayList<String>());
assign3(new ArrayList<String>());
// Both forms are acceptable as List<?>:
List<?> wildList = new ArrayList();
wildList = new ArrayList<String>();
assign1(wildList);
assign2(wildList);
assign3(wildList);
}
}
List表示“持有任何Object类型的原生List”,List<?>表示“具有某种特定类型的非原生List”。
List<?>和List<? extends Object>大致是等价的。
所以我们应当理解泛型的含义就是:泛化的类型,适用于很多的类型。通过泛型我们可以编写出更“泛化”的代码,这些代码对于它能作用的类型具有更少的限制,使得单个的代码段可以应用到更多的类型上。
Java代码
public class Holder {
private Object a;
public Holder(Object a) { this.a = a; }
public void set(Object a) { this.a = a; }
public Object get() { return a; }
public static void main(String[] args) {
Holder h2 = new Holder("The String");
String s = (String)h2.get();
h2.set(1);
Integer x = (Integer)h2.get();
}
}
虽然我们可以实现了可以同时持有不同的类型,但是我们取出时必须进行强制类型转化,如果不小心还容易产生CLASSCASTEXCEPTION,如果我们使用泛型的话,可以采用下面的实现方式。由上海java培训机构推荐阅读
Java代码
class Automobile{}
public class GenericHolder<T> {
private T a;
public GenericHolder(T a) { this.a = a; }
public void set(T a) { this.a = a; }
public T get() { return a; }
public static void main(String[] args) {
GenericHolder<Automobile> h3 =
new GenericHolder<Automobile>(new Automobile());
Automobile a = h3.get(); // No cast needed
// h3.set("The String"); // Error
// h3.set(1); // Error
}
}
我们通过将想要持有的对象预先放在尖括号内,编译器就会对放置的类型进行检测,防止出错。泛型应用于接口与泛型类差不多,这里就不在解释了。
泛型也可以应用于方法,泛型方法使得方法能够独立于类产生变化。泛型方法与其所处的类是否为泛型是没有任何关系的。
Java代码
public class GenericMethods {
public <T> void f(T x) {
System.out.println(x.getClass().getName());
}
public static void main(String[] args) {
GenericMethods gm = new GenericMethods();
gm.f("");
gm.f(1);
gm.f(1.0);
gm.f(1.0F);
gm.f('c');
gm.f(gm);
}
}
对比以上的泛型方法与泛型类,可以看出:当使用泛型类是,必须在创建对象的时候指定类型参数的值,而使用泛型方法时,通常不用指明参数的类型;编译器可以通过类型推断为我们找出具体的类型(只在赋值操作时起作用)。其他的时候也需要显示的指明类型,此时可以在点操作符与方法名之间插入尖括号例如:
Java代码
gm.<GenericMethods>f(gm);
可以看出泛型方法较泛型类更加的简洁和容易理解,所以我们在编程时应当尽量的使用泛型方法。
以上为泛型的基础应用,在学习泛型时,看到下面一处奇怪的代码:
Java代码
public class ErasedTypeEquivalence {
@SuppressWarnings("rawtypes")
public static void main(String[] args) {
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2);
}
}
对于ArrayList<String>和ArrayList<Integer>我们很容易认为它们是不同的类型,的确它们是不同的类型然而程序的输出却是"TRUE";这是由于Java的泛型是使用擦出来实现的,当我们使用泛型时,任何具体的类型信息都被擦除了,List<String>和List<Integer>在运行时都被擦出为成原生类型List了。
然而Java为什么要这么做呢?我们前面提到泛型在JAVA SE5之后才出现的,Java泛型使用擦除的原因在于向前兼容。即即使类库进行了代码改造使用了新的泛型实现,客户端依然可以在不改变代码的前提下继续使用这个类库,使得在不破坏现有类库的前提下,将泛型融入到了java语言中。更多Java相关请移步http://www.shaccp.cn/
然而通过擦除实现了Java中的泛型,必然会付出一定的代价,使得Java中的泛型不能用于显示地引用运行时类型的操作之中,例如:转型、instanceof操作和new表达式,而这些在C++中是可以使用的,在java中我们可以通过反射来间接的实现。由于擦出的存在下面这种方式试图重载方法是不正确的:
Java代码
public class UseList<W, T> {
//错误:Method f(List<T>) has the same erasure f(List<E>) as another method in type UseList<W,T>
void f(List<T> v){}
void f(List<W> v){}
}
所以我们应当注意的是java中的泛型只有在静态类型检查期间才会出现,在此之后,程序中所有的泛型类型都将被擦出,替换为它的非泛型边界。
什么边界?
边界使得我们可以在用于泛型的参数类型上设置限制条件:一方面使得我们可以规定泛型参数可以使用的类型,另一方面使得我们可以按照自己的边界类型来调用方法。例如:
Java代码
class HasF {
public void f() {
System.out.println("HasF.f()");
}
}
class Manipulator<T> {
private T obj;
public Manipulator(T x) {
obj = x;
}
// Error: 未发现方法f()
public void manipulate() {
obj.f();
}
}
public class Manipulation {
public static void main(String[] args) {
HasF hf = new HasF();
Manipulator<HasF> manipulator = new Manipulator<HasF>(hf);
manipulator.manipulate();
}
}
由于使用了擦除,Java编译器不能obj可以调用方法f()映射到HasF拥有方法f()这个情况上,此时我们可以使用边界,例如:
Java代码
class Manipulator2<T extends HasF> {
private T obj;
public Manipulator2(T x) {
obj = x;
}
public void manipulate() {
obj.f();
}
}
此时obj可以直接调用方法f()了。然后我们可以看出此时这样的泛型并没有给我们任何好处,我们不使用泛型,只需将Manipulator<T>中的T替换为HasF并且使用原生类Manipulator也可以实现以上的功能。所以使用泛型时我们最好自己的思考一下,我们的问题是否足够的复杂到必须使用泛型的程度,也就是说我们编写的这部分代码能够跨多个类进行工作。
我们看到
Java代码
class Manipulator2<T extends HasF>
这里的extends与我们在普通情况下看到的是不同的,因为java泛型重用了extends关键字。
我们继续分析下面一个比较难以理解的例子:
Java代码
class Fruit {}
class Apple extends Fruit {}
class Jonathan extends Apple {}
class Orange extends Fruit {}
public class GenericsAndCovariance {
public static void main(String[] args) {
//编译错误:Type mismatch: cannot convert from ArrayList<Apple> to List<Fruit>
//List<Fruit> flist = new ArrayList<Apple>();
List<? extends Fruit> flist = new ArrayList<Apple>();
// 编译错误:不能添加任何类型
//flist.add(new Apple());
// flist.add(new Fruit());
// flist.add(new Object());
flist.add(null); // Legal but uninteresting
Fruit f = flist.get(0);
List<Fruit> flist2 = new ArrayList<>();
flist2.add(new Apple());
}
}
如下:List<Fruit>表示持有Fruit以及Fruit的子类型,我们可以向flist2中添加Fruit的子类型Apple,然而Apple的List在类型上并不等价于Fruit的List
Java代码
List<Fruit> flist = new ArrayList<Apple>();
再看flist类型为List<? extends Fruit>我们可以理解为“具有任何从Fruit继承的某种类型的列表” 通配符表示flist指向某种没有指定的具体类型,听着有些拗口,这里?指某种而不是某些。如果允许以下操作:
Java代码
flist.add(new Apple());
flist.add(new Fruit());
flist.add(new Object());
我们在取出时由于不知道flist中的具体类型,又必须进行强制类型转换,这与泛型的思想又是背道而驰的。
所以我们在使用<? extends SomeClass>时,意味着我们只能通过这个名称来获取或者移除某些信息,而不能增加它的信息。
如果想要写入或者增加信息我们可以使用超类行通配符<? super SomeClass>,例如:
Java代码
public class SuperTypeWildcards {
static void writeTo(List<? super Apple> apples) {
apples.add(new Apple());
apples.add(new Jonathan());
// apples.add(new Fruit()); // Error
}
}
泛型中还存在另一种无界通配符<?>,意味着任何事物”
Java代码
public class UnboundedWildcards1 {
@SuppressWarnings("rawtypes")
static List list1;
static List<?> list2;
static List<? extends Object> list3;
static void assign1(@SuppressWarnings("rawtypes") List list) {
list1 = list;
list2 = list;
list3 = list; // Warning: unchecked conversion
// Found: List, Required: List<? extends Object>
}
static void assign2(List<?> list) {
list1 = list;
list2 = list;
list3 = list;
}
static void assign3(List<? extends Object> list) {
list1 = list;
list2 = list;
list3 = list;
}
@SuppressWarnings("rawtypes")
public static void main(String[] args) {
list1.add(new Integer(1));
// 错误list2与list3中只能添加null
// list2.add(new Integer(1));
// list3.add(new Integer(1));
assign1(new ArrayList());
assign2(new ArrayList());
assign3(new ArrayList()); // Warning:
// Unchecked conversion. Found: ArrayList
// Required: List<? extends Object>
assign1(new ArrayList<String>());
assign2(new ArrayList<String>());
assign3(new ArrayList<String>());
// Both forms are acceptable as List<?>:
List<?> wildList = new ArrayList();
wildList = new ArrayList<String>();
assign1(wildList);
assign2(wildList);
assign3(wildList);
}
}
List表示“持有任何Object类型的原生List”,List<?>表示“具有某种特定类型的非原生List”。
List<?>和List<? extends Object>大致是等价的。
所以我们应当理解泛型的含义就是:泛化的类型,适用于很多的类型。通过泛型我们可以编写出更“泛化”的代码,这些代码对于它能作用的类型具有更少的限制,使得单个的代码段可以应用到更多的类型上。
0 0
- 【java 2】java泛型
- Java 泛型 Java generic
- Java Tutorials_Generics(java泛型)
- Java基础 Java 泛型
- java 泛型
- java泛型
- Java泛型
- Java泛型
- java泛型
- java泛型
- java泛型
- Java 泛型
- Java泛型
- Java 泛型
- JAVA 泛型
- java 泛型
- java泛型
- Java泛型
- 发生的阵列
- Linux进程实时IO监控iotop命令详解
- 正则 断言总结
- Java基础视频教程-基础语法2
- android push notification[转]
- java泛型
- 设计的沟通与协作
- Android编程实战--安全卫士(1)
- Linux 查看系统硬件信息(实例详解)
- C/C++连接MySql数据库
- p里面的字自动换行
- JVM调优总结 -Xms -Xmx -Xmn -Xss
- git/gitHub 基本使用
- Sublime Text 2中文乱码问题的解决