【Java】java高级之泛型

泛型概念引入

有一个问题,如果我们需要产生多个对象,每个对象的逻辑完全一样,只是对象内的成员的类型不同。那么我们如何去做?

我们看下面代码:

class Cls1
{
	int a;
	
	public Cls1(int a){
		this.a = a;
	}
	
	public int getData(){
		return a;
	}
}

class Cls2
{
	String a;
	
	public Cls2(String a){
		this.a = a;
	}
	
	public String getData(){
		return a;
	}
}

public class Test {
	public static void main(String[] args) {
		Cls1 cls1 = new Cls1(10);
		System.out.println(cls1.getData());
		
		Cls2 cls2 = new Cls2("yangchenyang");
		System.out.println(cls2.getData());
	}
}

这段代码中有两个类,这两个类除了变量的类型不同,其他的部分都相同。如果我们现在还需要更多的变量类型不同,但实现逻辑相同的类的话,那么我们就需要定义更多的类,很容易看出这样会使代码变得冗杂,导致类的膨胀,重用性太差。

既然这样,我们就有另一种方法:即创建一个类文件,给这个类中的成员变量设置Object数据类型。

class Cls
{
	Object a;
	
	public Cls(Object a){
		this.a = a;
	}

	public Object getData(){
		return a;
	}
}



public class Test {
	public static void main(String[] args) {
		Cls cls1 = new Cls(10);
		System.out.println(cls1.getData());
		
		Cls cls2 = new Cls("yangchenyang");
		System.out.println(cls2.getData());
	}
}

这种方法利用了Object是所有类型的父类的特点,可以将它根据需要随意进行转换,但是它也有缺点:编译的时候正常,但运行的时候可能会异常。

对于这种情况的发生,我们有很好的解决办法,就是泛型



泛型简介(JDK1.5以后)

  • 泛型可以在编译的时候检查类型安全,并且所有的强制类型转换都是自动和隐式的。
  • 泛型的原理就是“数据的参数化”,即把类型看作整数。也就是说把所要操作的数据类型看作参数,就像方法的形式参数是运行时传递的值的占位符一样。
  • 简单的说,类型变量扮演的角色就如同一个参数,它提供给编译器用来类型检查的信息。
  • 泛型可以提高代码的扩展性和重用性。

在这里插入图片描述

根据泛型的用法,上面的代码可以再进行修改:

class Cls<T>
{
	T a;
	
	public Cls(T a){
		this.a = a;
	}

	public T getData(){
		return a;
	}
}



public class Test {
	public static void main(String[] args) {
		Cls<Integer> cls1 = new Cls<Integer>(10);
		System.out.println(cls1.getData());
		
		Cls<String> cls2 = new Cls<String>("yangchenyang");
		System.out.println(cls2.getData());
	}
}



泛型类及特点

  1. 泛型的类型参数可以是泛型类

class Cls<T>
{
	T a;
	
	public Cls(T a){
		this.a = a;
	}

	public T getData(){
		return a;
	}
}

public class Test {
	public static void main(String[] args) {
		//泛型类里的类型还是泛型,构造方法参数应该为泛型类实例化后的对象
		Cls<Cls<Integer>> cls1 = new Cls<Cls<Integer>>(new Cls<Integer>(100));	
		System.out.println(cls1.getData().getData());	//实则为泛型的嵌套
		
	}
}
  1. 泛型类可以同时设置多个类型参数
class Cls1<T, T1>
{
	T a;
	T1 b;
	public Cls1(T a, T1 b){
		this.a = a;
		this.b = b;
	}

	public T getData(){
		return a;
	}
	
	public T1 getData1(){
		return b;
	}
}

public class Test {
	public static void main(String[] args) {
		Cls1<Integer, String> cls3 = new Cls1<Integer, String>(100, "帅");	//两个泛型类型的实例化
		System.out.println(cls3.getData()+cls3.getData1());		//字符串和整型相加是连接
		//System.out.println(cls3.getData1());
		
		Cls1<Integer, Integer> cls4 = new Cls1<Integer, Integer>(100, 10);
		System.out.println(cls4.getData()+cls4.getData1());
		//System.out.println(cls3.getData1());
	}
}

  1. 泛型类可以继承泛型类
  2. 泛型类可以实现泛型接口
abstract class Cls<T>
{
	T a;
	
	public Cls(T a){
		this.a = a;
	}

	public T getData(){
		return a;
	}
	
	abstract void printInfo();
}

interface Cls2<T>
{
	abstract void printInfoCls2(T t);
}
  
class Cls1<T, T1> extends Cls<T> implements Cls2<T>
{
	T1 b;
	public Cls1(T a, T1 b){
		super(a);	//先调用父类的构造方法
		this.b = b;
	}
	
	public T1 getData1(){
		return b;
	}
	
	void printInfo(){	//实现父类中的抽象方法
		System.out.println("父类抽象方法的实现");
	}

	public void printInfoCls2(T t) {	//实现接口中的方法
		// TODO Auto-generated method stub
		System.out.println(t);
	}
}

public class Test {
	public static void main(String[] args) {
		
		
		Cls1<Integer, String> cls2 = new Cls1<Integer, String>(10, "yangchenyang");
		System.out.println(cls2.getData());
		System.out.println(cls2.getData1());
		cls2.printInfo();	//调用实现了的父类抽象方法
		cls2.printInfoCls2(100);	//调用实现了的接口中的方法
		
	}
}



限制泛型可用类型

在定义泛型类别时,默认在实例化泛型类的时候可以使用任何类型,但是如果想要限制使用泛型类型时,只能用某个特定类型或者是其子类型才能实例化该类型时,可以在定义类型时,使用extends关键字指定这个类型必须是继承某个类,或者实现某个接口。

当没有指定泛型继承的类型或接口时,默认使用extends Object,所以默认情况下任何类型都可以作为参数传入。

class Animal
{
	
}

class Dog extends Animal
{
	
}

interface Move
{
	abstract void test();
}

abstract class Cls<T extends String>	//限制泛型可用类型为String
{
	T a;
	
	public Cls(T a){
		this.a = a;
	}

	public T getData(){
		return a;
	}
	
	abstract void printInfo();
}

interface Cls2<T>
{
	abstract void printInfoCls2(T t);
}
  
class Cls1<T extends String, T1 extends Dog> extends Cls<T> implements Cls2<T>	//限制泛型T1继承Dog类
{
	T1 b;
	public Cls1(T a, T1 b){
		super(a);	//先调用父类的构造方法
		this.b = b;
	}
	
	public T1 getData1(){
		return b;
	}
	
	void printInfo(){	//实现父类中的抽象方法
		System.out.println("父类抽象方法的实现");
	}

	public void printInfoCls2(T t) {	//实现接口中的方法
		// TODO Auto-generated method stub
		System.out.println(t);
	}
}

public class Test {
	public static void main(String[] args) {
		Cls1<String, Dog> cls2 = new Cls1<String, Dog>("chenyangchen", new Dog());	//被限制的泛型的参数
	}
}

注意:泛型继承接口的时候要用关键字 extends。



泛型通配的方式

类型通配声明

同一泛型类,如果实例化给定的实际类型不同,则这些实例的类型时不兼容的,不能相互赋值。

Generic<Boolean>f1 = new Generic<Boolean>();
Generic<Integer>f2 = new Generic<Integer>();
f1 = f2;	//发生编译错误
Generic<Object>f = f1;	//f1和f类型并不兼容,发生编译错误
f = f2;		//f1和f类型同样不兼容,也会发生编译错误

泛型类实例之间的不兼容性会带来使用的不便。我们可以使用泛型通配符(?)声明泛型类的变量就可以解决这个问题。

泛型通配的方式

  • ”?“表示任意一个类型
Generic<Boolean>f1 = new Generic<Boolean>();
Generic<?> f = f1;
  • 和限制泛型的上限相似,同样可以使用extends关键字限定通配符的上限
Generic<Dog>f1 = new Generic<Dog>();
Generic<? extends Animal> f = f1;
  • 还可以使用super关键字将通配符匹配类型限定为某个类型及其父类型
Generic<Animal>f1 = new Generic<Animal>();
Generic<? super Dog> f = f1;

案例代码


class Animal
{
	
}

class Dog extends Animal
{
	
}


class Cls<T>	//限制泛型可用类型为String
{
	T a;
	
	public Cls(T a){
		this.a = a;
	}

	public T getData(){
		return a;
	}
	
}


public class Test {
	public static void main(String[] args) {
		Cls<Integer> c1 = new Cls<Integer>(10);
		Cls<Double> c2 = new Cls<Double>(10.0);
		Cls<? extends Animal> c3;
		
		Cls<Dog> c4 = new Cls<Dog>(new Dog());
		
		c3 = c1;	//编译报错
		c3 = c2;	//编译报错
		c3 = c4;	//c3可以通配Animal的子类
	}
}



泛型方法

用法和特点

  • 不仅类可以声明类型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法。其定义格式为:
访问修饰符 <泛型列表> 返回类型 方法名(参数列表){
	实现代码
}
  • 在泛型列表中声明的泛型,可用于该方法的返回类型声明、参数类型声明和方法代码中的局部变量的类型声明。
  • 类中其他方法不能使用当前方法声明的类型。

提示:是否拥有泛型方法,于其所在的类是否有泛型没有关系。要定义泛型方法,只需将泛型参数列表置于返回值前。

例程代码


class A<T>
{
	public void printInfo(T t){
		System.out.println(t);
	}
}

class B
{
	public <T> T printInfo(T t){		//这里是泛型方法,返回值可以为泛型类型
		System.out.println(t);
		
		return t;
	}
	
	public <T, T2> void printInfo(T t, T2 t2){	//这里是泛型方法的重载
		System.out.println(t);
		System.out.println(t2);
		
//		System.out.println(t2);		//报错,不让加,因为没有实例化,不能确定泛型的类型
		
	}
}

public class Test {
	public static void main(String[] args) {
		A<String> a = new A<String>();
		a.printInfo("哈哈");	//已经限定参数类型为字符串,不能打印其他的
		//a.printInfo(1234);	//已经限定参数类型为字符串,报错
		
		B b = new B();
		b.printInfo("哈哈");	//泛型方法类似于方法的重载,但比其要更灵活
		b.printInfo(1234);
		b.printInfo(0.5);
		b.printInfo('c');
	}
}

什么时候使用泛型方法,而不使用泛型类呢?

  • 添加类型约束只作用于一个方法的多个参数之间、而不涉及到类中的其他方法时。
  • 施加类型约束的方法为静态方法,只能将其定义为泛型方法,因为静态方法不能使用其所在类的类型参数。

泛型方法限制泛型可用类型

class Animal
{
	public void eat(){
		System.out.println("动物吃");
	}
}

class Dog extends Animal
{
	public void eat(){
		System.out.println("啃骨头");
	}
}

class B
{
	public static <T extends Animal> void printInfo2(T t){		//泛型方法中的类型约束,这种方法被称为静态方法
		t.eat();
	}
}

public class Test {
	public static void main(String[] args) {
		B b = new B();

		b.printInfo2(new Dog());
		B.printInfo2(new Dog());
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT阳晨。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值