工作笔记-Java 泛型和泛型参数

泛型是Java1.5以后的特性,在各种开源框架的源码当中可以看到很多泛型的使用,如果不对泛型做到了如指掌的话,看源码真的会有阻碍。下面是泛型的一个简单的例子。

public class GenericsDemo<T> {
	private T demoProp;
	
	public T getDemoProp(){
		return demoProp;
	}
	
	public void setDemoProp(T demoProp){
		this.demoProp = demoProp;
	}
}

GenericsDemo类声明了一个泛型参数,T可以代表任何一个类型,在编译时,编译器并不知道对象在实例化过程中使用的T类型到底是什么,泛型是运用在编译时期的技术。

GenericsDemo的成员变量demoProp是T类型的,这个变量的类型也是在对象实例化的过程中才会指定的。

	public static void main(String args[]){
		GenericsDemo<String> demoStr = new GenericsDemo<String>();
		GenericsDemo<Integer> demoInt = new GenericsDemo<Integer>();
		GenericsDemo<Double> demoDoub = new GenericsDemo<Double>();
		
		String str = demoStr.getDemoProp();
		Integer inte = demoInt.getDemoProp();
		Double doub = demoDoub.getDemoProp();
	}

在使用过程中,分别指定String, Integer, Double为参数对GenericsDemo进行实例化,然后分别用这三个类型去接收demoProp的值,不会有error或者warning。

但是如果使用比如int会报错,因为泛型参数只能替换为对象类型,而int类型是基础数据类型,不是对象。

		GenericsDemo<int> demoInt = new GenericsDemo<int>();//编译报错

接收的变量类型和实例化对象时使用的泛型参数类型不匹配时也会报错

		String inte = demoInt.getDemoProp();//编译报错

类声明时可以指定不止一个泛型参数

public class GenericsDemo<T,E> {
	private T demoProp;
	
	public T getDemoProp(){
		return demoProp;
	}
	
	public void setDemoProp(T demoProp){
		this.demoProp = demoProp;
	}
	
	public void process(E element){
		System.out.println("processing");
	}

}

如果我们预设实例化时,T,E传入的是某一类对象,并且想使用对象的方法和属性,我们就必须对T,E进行限定。不做限定的T,E只能使用Object的方法,因为所有对象都派生自Object。

public class GenericsDemo<E> {
	
	public void process(E element){
        //可以使用Object的getClass()方法
		System.out.println(element.getClass().getName());
	}

}

可以使用extends和super对泛型参数添加限定,extends的使用举例,定义People类,实现了Action接口。

public class People implements Action{
	
	public void walk() {
		System.out.println("walking");
	}

}
public interface Action {

	public void walk();
}
public class Male extends People{

	public void shave(){
		System.out.println("only man can shave");
	}
}

重新定义GenericsDemo

public class GenericsDemo<T extends People> {
	private T demoProp;
	
	public T getDemoProp(){
		return demoProp;
	}
	
	public void setDemoProp(T demoProp){
		this.demoProp = demoProp;
	}
	
	public void move(){
		demoProp.walk();
	}
}

<T extends People>代表泛型参数T是People的子类型,对T进行限定了以后,编译器知道T肯定是People的子类型,就可以使用People的方法了。上例中通过demoProp.walk()直接调用。

实例化GenericsDemo时,可以传入People或者其子类

public static void main(String args[]){
	GenericsDemo<People> demoPeople = new GenericsDemo<People>();
	GenericsDemo<Male> demoMale = new GenericsDemo<Male>();
	demoPeople.setDemoProp(new People());
	demoPeople.move();
		
	demoMale.setDemoProp(new Male());
	demoMale.move();
}

没有报错,运行正常,说明extends边界包含T本身。

但是要注意的是,extends和我们继承时候使用的extends不是相同的,这里的extends是子类型的意思,不是子类,对于接口也是可行的,比如这么写


public class GenericsDemo<T extends Action> {
	private T demoProp;
	
	public T getDemoProp(){
		return demoProp;
	}
	
	public void setDemoProp(T demoProp){
		this.demoProp = demoProp;
	}
	
	public void move(){
		demoProp.walk();
	}

}

依然能够正常编译和运行,是因为People和Male都实现了Action接口,也是Action派生出来的类。

super和extends相反,通过super限定的泛型参数,比如<? super Male>,代表泛型参数是指定类型的父类型。

super只能用在泛型参数,不能用在类声明中,很好理解,对于extends的情况,编译器知道继承的具体父类型是谁,所以自然对象可以使用哪些方法是具体的。对于super的情况,编译器并不知道父类具体是谁,所以父类型有哪些方法编译器是不知道的,所以在类的声明中,<? super Male> 和<T>在编译器看来没什么区别。

//编译报错
public class GenericsDemo<T super Male> {
}

通配符?

在泛型类的使用中,有时候不知道最后传入的参数是什么类型的对象,会使用通配符?来代替,比如

	public static void main(String args[]){
		GenericsDemo<?> demoPeople;
		demoPeople = new GenericsDemo<People>();
		demoPeople = new GenericsDemo<Male>();

		demoPeople.move();
		}

比如我们使用?来声明demoPeople,没有给他指定特定的对象类型,所以他的对象类型是可以任意的,所以可以使用People和Male来初始化他,都不会报错。

?不能出现在等号右边,必须告诉编译器你打算用什么类型初始化。

?通配符声明的对象在初始化以后不能对泛型成员变量进行set。

//报错
demoPeople = new GenericsDemo<?>();
//报错
demoPeople.setDemoProp(new People());

通配符和限定词配合使用


public class GenericsESDemo {
	public void testSuper(GenericsDemo<? super Male> generic){
		System.out.println("testing super");
	}
	
	public void testExtends(GenericsDemo<? extends People> generic){
		System.out.println("testing extends");
	}
	
	
	public static void main(String args[]){
		GenericsESDemo demo = new GenericsESDemo();
		demo.testSuper(new GenericsDemo<People>());
		demo.testExtends(new GenericsDemo<Male>());
		
	}
}

可以限制传入的泛型参数是某些特定类型的父类型或者子类型,使用可以更加灵活。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java泛型Java 5引入的新特性,可以提高代码的可读性和安全性,降低代码的耦合度。泛型是将类型参数化,实现代码的通用性。 一、泛型的基本语法 在声明类、接口、方法时可以使用泛型泛型的声明方式为在类名、接口名、方法名后面加上尖括号<>,括号中可以声明一个或多个类型参数,多个类型参数之间用逗号隔开。例如: ```java public class GenericClass<T> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } public interface GenericInterface<T> { T getData(); void setData(T data); } public <T> void genericMethod(T data) { System.out.println(data); } ``` 其中,`GenericClass`是一个泛型类,`GenericInterface`是一个泛型接口,`genericMethod`是一个泛型方法。在这些声明中,`<T>`就是类型参数,可以用任何字母代替。 二、泛型的使用 1. 泛型类的使用 在使用泛型类时,需要在类名后面加上尖括号<>,并在括号中指定具体的类型参数。例如: ```java GenericClass<String> gc = new GenericClass<>(); gc.setData("Hello World"); String data = gc.getData(); ``` 在这个例子中,`GenericClass`被声明为一个泛型类,`<String>`指定了具体的类型参数,即`data`字段的类型为`String`,`gc`对象被创建时没有指定类型参数,因为编译器可以根据上下文自动推断出类型参数为`String`。 2. 泛型接口的使用 在使用泛型接口时,也需要在接口名后面加上尖括号<>,并在括号中指定具体的类型参数。例如: ```java GenericInterface<String> gi = new GenericInterface<String>() { private String data; @Override public String getData() { return data; } @Override public void setData(String data) { this.data = data; } }; gi.setData("Hello World"); String data = gi.getData(); ``` 在这个例子中,`GenericInterface`被声明为一个泛型接口,`<String>`指定了具体的类型参数,匿名内部类实现了该接口,并使用`String`作为类型参数。 3. 泛型方法的使用 在使用泛型方法时,需要在方法名前面加上尖括号<>,并在括号中指定具体的类型参数。例如: ```java genericMethod("Hello World"); ``` 在这个例子中,`genericMethod`被声明为一个泛型方法,`<T>`指定了类型参数,`T data`表示一个类型为`T`的参数,调用时可以传入任何类型的参数。 三、泛型的通配符 有时候,我们不知道泛型的具体类型,可以使用通配符`?`。通配符可以作为类型参数出现在方法的参数类型或返回类型中,但不能用于声明泛型类或泛型接口。例如: ```java public void printList(List<?> list) { for (Object obj : list) { System.out.print(obj + " "); } } ``` 在这个例子中,`printList`方法的参数类型为`List<?>`,表示可以接受任何类型的`List`,无论是`List<String>`还是`List<Integer>`都可以。在方法内部,使用`Object`类型来遍历`List`中的元素。 四、泛型的继承 泛型类和泛型接口可以继承或实现其他泛型类或泛型接口,可以使用子类或实现类的类型参数来替换父类或接口的类型参数。例如: ```java public class SubGenericClass<T> extends GenericClass<T> {} public class SubGenericInterface<T> implements GenericInterface<T> { private T data; @Override public T getData() { return data; } @Override public void setData(T data) { this.data = data; } } ``` 在这个例子中,`SubGenericClass`继承了`GenericClass`,并使用了相同的类型参数`T`,`SubGenericInterface`实现了`GenericInterface`,也使用了相同的类型参数`T`。 五、泛型的限定 有时候,我们需要对泛型的类型参数进行限定,使其只能是某个类或接口的子类或实现类。可以使用`extends`关键字来限定类型参数的上限,或使用`super`关键字来限定类型参数的下限。例如: ```java public class GenericClass<T extends Number> { private T data; public T getData() { return data; } public void setData(T data) { this.data = data; } } public interface GenericInterface<T extends Comparable<T>> { T getData(); void setData(T data); } ``` 在这个例子中,`GenericClass`的类型参数`T`被限定为`Number`的子类,`GenericInterface`的类型参数`T`被限定为实现了`Comparable`接口的类。 六、泛型的擦除 在Java中,泛型信息只存在于代码编译阶段,在编译后的字节码中会被擦除。在运行时,无法获取泛型的具体类型。例如: ```java public void genericMethod(List<String> list) { System.out.println(list.getClass()); } ``` 在这个例子中,`list`的类型为`List<String>`,但是在运行时,`getClass`返回的类型为`java.util.ArrayList`,因为泛型信息已经被擦除了。 七、泛型的类型推断 在Java 7中,引入了钻石操作符<>,可以使用它来省略类型参数的声明。例如: ```java List<String> list = new ArrayList<>(); ``` 在这个例子中,`ArrayList`的类型参数可以被编译器自动推断为`String`。 八、总结 Java泛型是一个强大的特性,可以提高代码的可读性和安全性,降低代码的耦合度。在使用泛型时,需要注意它的基本语法、使用方法、通配符、继承、限定、擦除和类型推断等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值