泛型目的
为了更好进行类型指定,在类型确定后,编译器就能发现类型是否使用正常。
泛型的使用
<T> //尖括号 包含泛型类型“T” T可以是任意字母
泛型类
package fanxing;
public class Fclass<T>{
private T something;
public Fclass(T something) {
this.something = something;
}
public void console(){
System.out.println("Fclass.console: " + something);
}
public static void main(String[] args) {
//TODO 泛型类使用
Fclass<String> fclass = new Fclass<>("String");
fclass.console(); //Fclass.console: String
// Fclass<String> fclass1 = new Fclass<>(123);//编写代码时编译器就会报错
Fclass<Integer> fclass1 = new Fclass<>(123);
fclass1.console(); //Fclass.console: 123
}
}
泛型方法
泛型方法可以声明在任意类中,是不受类影响的
public class Gen1{ //这是个普通类
public <T> void show(T t){
}
}
public class Fclass<T>{
private T something;
public Fclass(T something) {
this.something = something;
}
public void console(){
System.out.println("Fclass.console: " + something);
}
//泛型方法
public <T> void show1(T t){
System.out.println("类的泛型成员变量something:" + something);
System.out.println("方法参数变量t:" + t);
}
public static void main(String[] args) {
//TODO 泛型类使用
Fclass<String> fclass = new Fclass<>("String");
fclass.console(); //Fclass.console: String
// Fclass<String> fclass1 = new Fclass<>(123);//编写代码时编译器就会报错
Fclass<Integer> fclass1 = new Fclass<>(123);
fclass1.console(); //Fclass.console: 123
Fclass<Integer> fclass2 = new Fclass<>(222);
/*
---------Log------------
类的泛型成员变量something:222
方法参数变量t:true
*/
fclass2.show1(true);
}
}
类型限定
package fanxing;
import java.lang.reflect.Array;
import java.util.List;
public class FLimit {
// 这里的 类型T 是实现了Comparable接口的
public <T extends Comparable> int compare(T t1, T t2){
return t1.compareTo(t2);
}
class Person{
}
interface Hand{
void touch();
}
class C1 extends Person implements Hand{
@Override
public void touch() {
}
}
// 表示类型T、V 派生于Person并实现了Hand接口的
// 注意:extends 时如果有类只能放最前面且只能有一个,接口不分顺序用 & 相连
public <T, V extends Person&Hand> void doSomething(T t1, V v1){
}
}
通配符使用
package fanxing;
class Employee{
}
class Worker extends Employee{
}
public class Pair<T>{
public static void set(Pair<Employee> employeePair){
}
//TODO 方法一:使用泛型方法,泛型限定类型
public static <E extends Employee> void set1(Pair<E> tPair){
}
//TODO 方法二:形参类型使用通配符对泛型类型进行限定
public static void set2(Pair<? extends Employee> tPair){
}
public static void main(String[] args) {
//employeePair 和 workerPair 不存在继承关系
Pair<Employee> employeePair = new Pair<>();
Pair<Worker> workerPair = new Pair<>();
System.out.println(employeePair.getClass().getName());
System.out.println(workerPair.getClass().getName());
System.out.println("Pair.main: "+ (employeePair.getClass() == workerPair.getClass()));
//TODO 错误示范
//set(workerPair); //报错,证明类型对不上
set(employeePair);
//TODO 方法一:使用泛型方法,泛型限定类型
set1(workerPair);
set1(employeePair);
//TODO 方法二:形参类型使用通配符对泛型类型进行限定
set2(workerPair);
set2(employeePair);
//TODO 推荐方法二,省得麻烦
}
}
通用代码
class Food {
}
class Fruit extends Food{
}
class Orange extends Fruit{
}
class Apple extends Fruit{
}
class Hongfushi extends Apple {
}
class GenericType<T> {
private T food;
public T getFood() {
return food;
}
public void setFood(T food) {
this.food = food;
}
}
extends
通配符 extends 关键字,只能做到安全访问数据操作,而无法进行设置。
public static void useExtends(){
// 写法一:
GenericType<? extends Fruit> fruitGenericType = new GenericType<Apple>();
// 写法二:
GenericType<? extends Fruit> genericType;
GenericType<Apple> appleGenericType = new GenericType<>();
GenericType<Hongfushi> hongfushiGenericType = new GenericType<>();
genericType = appleGenericType;
genericType = hongfushiGenericType;
//报错,extends只能进行安全取,
// 试想一下,编译器怎么知道你设置的是什么水果类型,
// 由于通配符类型实例genericType 可以是Fruit及其任意子类,
// 例如:当genericType = appleGenericType 时就是说,类型为苹果,所以进行设置的时候是不是设置的参数为苹果类型的,
// 同理,genericType 也可以是红富士类型,所以说,这种情况编译器就不知道怎么确定你的类型参数,就无法进行设置。
//fruitGenericType.setFood(new Apple());
//如果知道的情况下可以进行类型强转,否则可能引发运行时错误
Apple apple = (Apple) fruitGenericType.getFood();
// 正常写法:
Fruit fruit = fruitGenericType.getFood();
}
由上述代码示例分析可知,由于通配符类型实例genericType 可以是Fruit及其任意子类,例如:
当genericType = appleGenericType 时就是说,类型为苹果,所以进行设置的时候是不是设置的参数为苹果类型的, 同理,genericType 也可以是红富士类型,所以说,这种情况编译器就不知道怎么确定你的类型参数,就无法进行设置。
super
通配符 super,可以对类型参数进行设置,因为知道此类型的下界在哪,所以编译器能知道其类型,同样也可以访问数据
package fanxing;
class Food {
}
class Fruit extends Food{
}
class Orange extends Fruit{
}
class Apple extends Fruit{
}
class Hongfushi extends Fruit {
}
class GenericType<T> {
private T food;
public T getFood() {
return food;
}
public void setFood(T food) {
this.food = food;
}
}
public class Tongpeifu {
private static void printSuper(GenericType<? super Apple> p){
System.out.println( p.getFood().getClass().getName() );
}
public static void useSuper(){
GenericType<Object> objectGenericType = new GenericType<>();
GenericType<Food> foodGenericType = new GenericType<>();
GenericType<Fruit> fruitGenericType = new GenericType<>();
GenericType<Apple> appleGenericType = new GenericType<>();
GenericType<Hongfushi> hongfushiGenericType = new GenericType<>();
GenericType<Orange> orangeGenericType = new GenericType<>();
objectGenericType.setFood(new Apple());
objectGenericType.setFood(new Fruit());
printSuper(objectGenericType);
// printSuper(foodGenericType);
/**
* 报错(原因)<br>
* 以这个水果类型为例,在设置时,是不是必须保证其类型是水果,
* 而如果类型是食物的话,能否保证它是水果,结果显而是不能的,所以会报错;
* 但是如果类型是苹果的话,就肯定能保证它属于水果类型
*/
//fruitGenericType.setFood(new Food()); //上面描述报错原因
fruitGenericType.setFood(new Fruit());
fruitGenericType.setFood(new Apple());
printSuper(fruitGenericType);
// printSuper(appleGenericType);
//printSuper(hongfushiGenericType);//报错:由于红富士是苹果的子类,而super限定类型为苹果的父类
//printSuper(orangeGenericType);//报错:和苹果平级,但是类型不对
}
public static void main(String[] args) {
useSuper();
}
}
虚拟机泛型实现
Java语言是使用类型擦除的方式来实现的,因为Java类中,依赖Object类进行派生出来的,所以能使用类型擦除来作为泛型的实现方式,并不是真泛型。