java泛型、泛型类及特点、限制泛型、类型通配符声明、泛型方法

java泛型:

  • 泛型简介
  • 泛型类
  • 限制泛型可用类型
  • 类型通配声明
  • 泛型方法

问题: 如果我们需要产生多个对象,每个对象的逻辑完全一样,只是对象内的成员变量的类型不同。那我们该如何去做?如果按照正常的做法就要创建多个类文件,给每个类中的成员变量设置指定的数据类型。这样做的缺点: 这样会导致类膨胀,重用太差。解决方法: 创建一个类文件,给这个类中2成员变量设置Object(是所有类的父类)数据类型。 缺点: 编译的时候正常,但运行的时候可能会出现异常。因为Object导致整个类对变量的属性很模糊。面对这样的问题在JDK1.5以后引入泛型

class Cla1
{
	Object a;
	public Cla1(Object a) {
		this.a=a;
	}
	public Object getData(){
		return a;
	}
}
//class cla2
//{
//	String a;
//	public cla2(String a) {
//		this.a=a;
//	}
//	public String getData(){
//		return a;
//	}
//}
public class Mian {
	public static void main(String[] args) {
		Cla1 c1=new Cla1("String");
		System.out.println(c1.getData());
		//System.out.println((Integer)c1.getData());Object导致整个类对变量的属性很模糊,在强制类型转换的时候会出现下列错误
		//Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
		//at Mian.main(Mian.java:25)
	}
}

泛型简介:

  • 泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动的和隐式的。
  • 泛型的原理就是 类型的参数化,即把类型看做参数。也就是说把所有要操作的数据类型看做参数,就像方法的形式参数是运行时传递的值的占位符一样。
  • 简单的说,类型变量 扮演的角色就如同一个参数,它是提供给编译器用来类型检查的信息。
  • 泛型可以提高代码的扩展性和重用性。
class Cla1<T>//T必须是引用类型
{
	T a;
	public Cla1(T a) {
		this.a=a;
	}
	public T getData(){
		return a;
	}
}
public class Mian {
	public static void main(String[] args) {
		Cla1<Integer> c1=new Cla1<Integer>(10);//明确类中参数的类型,相当于类型的参数化,用<>表示
		//Cla1 c1=new Cla1(10);这样写会报错,<>括号内的必须是引用类型,
		System.out.println(c1.getData());
	}
}

泛型类及特点:

  • 泛型的类型参数可以是泛型类
  • 泛型类可以同时设置多个类型参数
  • 泛型类可以继承泛型类
  • 泛型类可以实现泛型接口

泛型类可以同时设置多个类型参数

class Cla1<T> //一个类型参数
{
	T a;
	public Cla1(T a) {
		this.a=a;
	}
	public T getData(){
		return a;
	}
}
class Cla2<T1,T2> //多个类型参数
{
	T1 a;
	T2 b;
	public Cla2(T1 a,T2 b) {
		this.a=a;
		this.b=b;
	}
	public T1 getDataA(){
		return a;
	}
	public T2 getDataB(){
		return b;
	}
}
public class Mian {
	public static void main(String[] args) {
		Cla1<Integer> c1=new Cla1<Integer>(10);
		System.out.println(c1.getData());
		
		Cla2<Integer,String> c2=new Cla2<Integer,String>(30,"输出");
		System.out.println(c2.getDataA());
		System.out.println(c2.getDataB());
		System.out.println(c2.getDataA()+c2.getDataB());//数字和字符串相加,+起连接作用
		
		Cla2<Integer,Integer> c3=new Cla2<Integer,Integer>(100,120);
		System.out.println(c3.getDataA());
		System.out.println(c3.getDataB());
		System.out.println(c3.getDataA()+c3.getDataB());//数字和数字相加,+就是数值相加											    //有的博客说这里的数值不能直接相加		
	}
}

泛型类型可以是泛型类:

class Cla1<T> //一个类型参数
{
	T a;
	public Cla1(T a) {
		this.a=a;
	}
	public T getData(){
		return a;
	}
}
public class Main {
	public static void main(String[] args) {
		Cla1<Cla1<Integer>> c1=new Cla1<Cla1<Integer>>(new Cla1<Integer>(10));
		//泛型类里面的类型还是泛型类,传参的时候传的是泛型类的引用
		System.out.println(c1.getData().getData());
		//c1.getData()是调用的当做参数传入的泛型类,Cla1 a,就相当于这样的
		//此时a是泛型类类型,需要再对a取内容才能得到真正的a的数值,也就是当做参数传进去的那个泛型类的构造方法的数值
		
				
	}
}

泛型类可以继承泛型类:

abstract class Cla1<T> //一个类型参数
{
	T a;
	public Cla1(T a) {
		this.a=a;
	}
	public T getData(){
		return a;
	}
	abstract void printInfo();
}
class Cla2<T,T2> extends Cla1<T> //Cla2继承Cla1同时还可以增加泛型的个数,这里增加了T2
{
	T2 b;
	public Cla2(T a,T2 b) {
		super(a);
		this.b=b;
	}
	public T2 getDataB(){
		return b;
	}
	@Override
	void printInfo() {//如果这个类也是抽象类的话,就不用在将父类里面的抽象方法给补全了
		System.out.println("继承的类是泛型抽象类,所以要在子类里面实现抽象方法的方法体");
	}
}
public class Main {
	public static void main(String[] args) {	
		Cla2<Integer,String> c2=new Cla2<Integer,String>(110,"String");
		System.out.println(c2.getData());
		System.out.println(c2.getDataB());
		c2.printInfo();
	}

泛型类可以实现继承泛型接口:

interface Cla1<T>
{
	abstract void printfInfo(T t);//在接口中public和abstract可以省略,默认就是public和abstract
}
class Cla2<T> implements Cla1<T>
{
	public void printfInfo(T t)
	{
		System.out.println("泛型类继承泛型接口");
	}
}
public class Main {
	public static void main(String[] args) {
		Cla2<String> c1=new Cla2<String>();
		c1.printfInfo("");
	}
}

限制泛型可用类型:

  • 在定义泛型类别的时候,默认在实例化泛型类的时候可以使用任何类型,但是如果要限制使用泛型类型时,只能用某个特定类型或者是其子类型才能实例化该类型时,可以在定义类型时,使用extends关键字指定这个类型必须是继承某个类或者实现某个接口。例如:class Cla11这里Move是接口interface move这就要求T是接口Move类型的。接口是一个模版,需要类来继承这个接口来实现才能实例化,所以说继承了Move这个接口的类都属于T extren Move。这里就要求是继承了Move接口的类的类型。class Cla1这里Animal是自己定义的一个类,那么T extends Animal表示T只能是Animal类型或者其子类类型。
  • 当没有指定泛型继承的类型或者接口时,默认使用extends Object,所以默认情况下任何类型都可以作为参数传入。

限制使用泛型类型,只能用某个特定类型或者是其子类型才能实例化该类型:

interface Cla1<T extends String>
{
	abstract void printfInfo(T t);//在接口中public和abstract可以省略,默认就是public和abstract
}
class Cla2<T extends String> implements Cla1<T>//限制泛型只能用某个特定类型或者是其子类型使用extends继承,限制使用泛型类型为String类型
{
	T str;
	public Cla2(T str) {
		this.str=str;
	}
	public void printfInfo(T t)
	{
		System.out.println(str);
		System.out.println("泛型类继承泛型接口");
	}
}
class Cla3<T extends String,T2 extends Integer > extends Cla2<T>
{
	T2 in;
	public Cla3(T str,T2 in) {
		super(str);
		this.in=in;
	}
	public void printfInfo(T t)
	{
		System.out.println("父类:"+str+" 子类:"+in);
	}
}
public class Main {
	public static void main(String[] args) {
		Cla2<String> c1=new Cla2<String>("父类");
		c1.printfInfo(" ");
		Cla3<String,Integer> c2=new Cla3<String,Integer>("子类",10);
		c2.printfInfo(" ");
	}
}

类型通配符声明:

  • 同一泛型类,如果实例化时给定的实际类型不同,则这些实例的类型是不兼容的,不能相互赋值。
  • 泛型类实例之间的不见兼容性会带来使用的不方便。我们可以使用泛型通配符声明泛型类的变量就可以解决这个问题。
  • 和限制泛型的上限相似,同样可以使用extends关键字限定通配类型的上限:比如:
  • 还可以使用super关键词将通配符匹配类型限定为某个类型及其父类型
class Animal
{}
class Dog extends Animal
{}
class Cla1<T>
{
	T a;
	public Cla1(T a) {
		this.a=a;
	}
	T getData(){
		return a;
	}
}
public class Main {
	public static void main(String[] args) {
		Cla1<String> c1=new Cla1<String>("String");
		System.out.println(c1.getData());
		Cla1<Double> c2=new Cla1<Double>(1.23);
		System.out.println(c2.getData());
		
		//c1=c2;不允许这样会报错,因为不是一个类型的
		Cla1<?> c3;//使用通配符,即可不用限制类型
		c3=c1;
		c3=c2;
		
		Cla1<? extends String> c4;
		c4=c1;
		//c4=c2;因为限制了为String类型所以会报错
		
		Cla1<Dog> c5=new Cla1<Dog>(new Dog());
		Cla1<Object> c7=new Cla1<Object>(new Object());
		Cla1<? super Dog> c6;//使用super关键词将通配符匹配类型限定为某个类型及其父类型,c6必须是Dog的父类
		c6=c5;
		
		Cla1<? super Animal> c8;
		c8=c7;
	}
}

泛型方法:

  • 不仅类可以声明泛型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法。其定义格式为:访问修饰符 <泛型列表> 返回类型 方法名(){}
  • 在泛型列表中声明的泛型,可用于该方法的返回类型声明、参数类型声明和方法代码中的局部变量的类型声明。
  • 类中的其他方法不能使用当前方法声明的泛型。
  • 泛型方法更加灵活,它可以不受泛型类的约束。比方法重载更加灵活。
  • 提示: 是否拥有泛型方法,与其所在的类是否是泛型类没有关系,要定义泛型方法,只需要将泛型参数列表置于返回值前。
  • 什么时候使用泛型方法,而不是泛型类呢? 添加类型约束条件只作用于一个方法的多个参数之间、而不涉及到类中的其他方法时。 施加类型约束的方法为静态方法,只能将其定义为泛型方法,因为静态方法不能使用其所在类的类型参数。
  • 注意: 同样泛型方法也可以对齐进行限制和在泛型类里面的限制一样。T在编译的时候会重置为该类型在类中声明是所继承的父类。比如: T printInfo(T t)T默认是继承Object的,所以在编译的时候会重置为Object,而在编译的时候会重置为Animal。
class Animal
{
	void eat()
	{
		System.out.println("动物吃");
	}
}
class Dog extends Animal
{
	void eat()
	{
		System.out.println("狗啃骨头");
	}
}
class A<T>
{
	void printInfo(T t){
		System.out.println(t);
	}
}
class B
{
	<T> T printInfo(T t){//T仅对本方法有效,可用于该方法的返回类型声明和该方法的参数类型声明
		T a;//可用于方法代码中的局部变量的类型声明
		System.out.println(t);
		return t;
	}
	<T,T2> void printInfo(T t,T2 t2){//一个泛型方法有多个泛型参数,这也是个泛型方法的重载
		//System.out.println(t+t2);这样写不允许,必须分开写,因为还没有实例化,类型不确定,不能相加
		System.out.println(t);
		System.out.println(t2);
	}
	<T extends Animal> void printInfo2(T t)//施加泛型方法的类型约束
	{
		t.eat();
	}
	
	static <T extends Animal> void printInfo3(T t)//施加泛型方法的类型约束
	{
		t.eat();
	}
}
public class Main {
	public static void main(String[] args) {
		A<String> a=new A<String>();
		a.printInfo("String");
		
		B b=new B();
		b.printInfo("String");
		b.printInfo('c');
		b.printInfo(1213);
		b.printInfo(1,"str");
		
		b.printInfo2(new Dog());
		b.printInfo2(new Animal());
		
		B.printInfo3(new Animal());
		B.printInfo3(new Dog());//静态方法本身就是和类是脱离的,可以直接通过类名来调用静态方法
	}
}

你可能感兴趣的:(JAVA,java,泛型,泛型类,泛型方法)