嵌入式之路,贵在日常点滴

                                                                ---阿杰在线送代码

目录

一、泛型简介

二、泛型类及特点

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

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

3、泛型类可以继承泛型类

4、泛型类可以实现泛型接口

三、限制泛型可用类型 

四、类型通配的方式

类型通配声明 例子 

类型通配的方式 

五、泛型方法 

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


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

  • 创建多个类文件,给每个类中的成员变量设置指定的数据类型。

        缺点:这种方式会导致类的膨胀,重用型太差。 

 
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("ajie");
		System.out.println(cls2.getData());
	}
}

运行结果:

10
ajie 

  • 创建一个类文件,给这个类中的成员变量设置Object数据类型。

       缺点,编译的时候正常,但运行的时候可能会异常。 (因为其类型比较模糊,有时候做了错误得类型转换就会导致运行程序时崩溃,编译没有错误)

鼠标定位到数据类型,右键 选择 Type Hierarchy

观察可以看出Object是所有数据类型的父类

class Cls1
{
	Object a;
	
	public Cls1(Object a){
		this.a = a;
	}
	
	public Object getData(){
		return a;
	}
}
 
public class Test {
	public static void main(String[] args) {
		Cls1 cls1 = new Cls1(10);
		System.out.println(cls1.getData());		
		
		Cls1 cls2 = new Cls1("ajie");
		System.out.println(cls2.getData());
	}
}

为解决此问题,我们引入 泛型

一、泛型简介

  • 泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的。

  • 泛型的原理就是"类型的参数化",即把类型看作参数。也就是说把所要操作的数据类型看作参数,就像方法的形式参数是运行时传递的值的占位符一样。

  • 简单的说,类型变量扮演的角色就如同一个参数,它提供给编译器用来类型检查的信息。

  • 泛型可以提高代码的扩展性和重用性。 

class Cls1<T>
{
	T a;
	
	public Cls1(T a){
		this.a = a;
	}
	
	public T getData(){
		return a;
	}
}
 
public class Test {
	public static void main(String[] args) {
		Cls1<Integer> cls1 = new Cls1<Integer>(10);
		System.out.println(cls1.getData());		
		
		Cls1<String> cls2 = new Cls1<String>("ajie");
		System.out.println(cls2.getData());
	}
}

运行结果:

10
ajie 

myeclipse工程环境出错:Set project compiler compliance settings to '1.5'

解决方案 

二、泛型类及特点

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

class Cls1<T>
{
	T a;
	
	public Cls1(T a){
		this.a = a;
	}
	
	public T getData(){
		return a;
	}
}
 
public class Test {
	public static void main(String[] args) {
		Cls1<Cls1<Integer>> cls1 = new Cls1<Cls1<Integer>>(new Cls1<Integer>(10));
		System.out.println(cls1.getData().getData());		
		
	}
}

运行结果: 

10

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

class Cls2<T,T2>
{
	T a;
	T2 b;
 
	public Cls2(T a,T2 b){
		this.a = a;
		this.b = b;
	}
	
	public T getData(){
		return a;
	}
	public T2 getData2(){
		return b;
	}
}
 
public class Test {
	public static void main(String[] args) {
		
		Cls2<Integer,String> cls3 = new Cls2<Integer,String>(100,"ajie");
		System.out.println(cls3.getData());
		System.out.println(cls3.getData2());
		System.out.println(cls3.getData()+cls3.getData2());
		
		Cls2<Integer,Integer> cls4 = new Cls2<Integer,Integer>(100,10);
		System.out.println(cls4.getData()+cls4.getData2());//注意,在以前我们这里用到的+都是链接作用
									//在这里+可以作为加法使用
		int sum = 0;
		sum = cls4.getData()+cls4.getData2();
		System.out.println(sum);
	}
}

运行结果:

100
ajie
100ajie
110
110 

3、泛型类可以继承泛型类

abstract class Cls1<T>
{
	T a;
	
	public Cls1(T a){
		this.a = a;
	}
	
	public T getData(){
		return a;
	}
	
	abstract void printInfo();
}
 
class Cls2<T,T2> extends Cls1<T>
{
	T2 b;
	
	public Cls2(T a,T2 b){
		super(a);//调用了父类的构造方法,这个语句必须放在子类构造方法的第一句
		this.b = b;
	}
	
	public T2 getData2(){
		return b;
	}
 
	@Override
	void printInfo() {
		// TODO Auto-generated method stub
		System.out.println("print");
	}
} 
 
public class Test {
	public static void main(String[] args) {
			
		Cls2<Integer,String> cls = new Cls2<Integer,String>(10,"ajie");
		System.out.println(cls.getData());
		System.out.println(cls.getData2());
		cls.printInfo();
	}
}

运行结果:

10
ajie
print

4、泛型类可以实现泛型接口

abstract class Cls1<T>
{
	T a;
	
	public Cls1(T a){
		this.a = a;
	}
	
	public T getData(){
		return a;
	}
	
	abstract void printInfo();
}
 
interface Cls3<T>
{
	abstract void printInfoCls3(T t);
}
 
class Cls2<T,T2> extends Cls1<T> implements Cls3<T>
{//如果Cls2的T和Cls3T是同名,则在下面主函数使用时,Cls3的类型遵循Cls2,否则会报错
 //即使不同名,也会报错 问题如何解决 在下面展示
	T2 b;
	
	public Cls2(T a,T2 b){
		super(a);//调用了父类的构造方法,这个语句必须放在子类构造方法的第一句
		this.b = b;
	}
	
	public T2 getData2(){
		return b;
	}
 
	@Override
	void printInfo() {
		// TODO Auto-generated method stub
		System.out.println("print");
	}
 
	@Override
	public void printInfoCls3(T t) {
		// TODO Auto-generated method stub
		System.out.println(t);
	}
} 
 
public class Test {
	public static void main(String[] args) {
			
		Cls2<Integer,String> cls = new Cls2<Integer,String>(10,"ajie");
		System.out.println(cls.getData());
		System.out.println(cls.getData2());
		cls.printInfo();
		cls.printInfoCls3(100);
	}
}

运行结果:

10
ajie
print
100 

如何解决 如果Cls2的T和Cls3的T是同名,则在下面主函数使用时,Cls3的T类型遵循Cls2,否则会报错 问题。即使不同名,也会报错

abstract class Cls1<T>
{
	T a;
	
	public Cls1(T a){
		this.a = a;
	}
	
	public T getData(){
		return a;
	}
	
	abstract void printInfo();
}
 
interface Cls3<T3>
{
	abstract void printInfoCls3(T3 t);
}
 
class Cls2<T,T2,T3> extends Cls1<T> implements Cls3<T3>
{//如果Cls2的T和Cls3T是同名,则在下面主函数使用时,Cls3的类型遵循Cls2,否则会报错
 
	T2 b;
	
	public Cls2(T a,T2 b){
		super(a);//调用了父类的构造方法,这个语句必须放在子类构造方法的第一句
		this.b = b;
	}
	
	public T2 getData2(){
		return b;
	}
 
	@Override
	void printInfo() {
		// TODO Auto-generated method stub
		System.out.println("print");
	}
 
	@Override
	public void printInfoCls3(T3 t) {
		// TODO Auto-generated method stub
		System.out.println(t);
	}
} 
 
public class Test {
	public static void main(String[] args) {
			
		Cls2<Integer,String,String> cls = new Cls2<Integer,String,String>(10,"ajie");
		System.out.println(cls.getData());
		System.out.println(cls.getData2());
		cls.printInfo();
		cls.printInfoCls3("ajie");
	}
}

三、限制泛型可用类型 

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

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

//默认情况下,以下两种情况是一样的
class Cls1<T>
{
	T a;
	
	public Cls1(T a){
		this.a = a;
	}
	
	public T getData(){
		return a;
	}
}
 
class Cls1<T extends Object>
{
	T a;
	
	public Cls1(T a){
		this.a = a;
	}
	
	public T getData(){
		return a;
	}
}
//使用extends关键字指定这个类型
 
abstract class Cls1<T extends String>
{
	T a;
	
	public Cls1(T a){
		this.a = a;
	}
	
	public T getData(){
		return a;
	}
	
	abstract void printInfo();
}
 
interface Cls3<T3>
{
	abstract void printInfoCls3(T3 t);
}
 
class Cls2<T extends String,T2,T3> extends Cls1<T> implements Cls3<T3>
{
	T2 b;
	
	public Cls2(T a,T2 b){
		super(a);//调用了父类的构造方法,这个语句必须放在子类构造方法的第一句
		this.b = b;
	}
	
	public T2 getData2(){
		return b;
	}
 
	@Override
	void printInfo() {
		// TODO Auto-generated method stub
		System.out.println("print");
	}
 
	@Override
	public void printInfoCls3(T3 t) {
		// TODO Auto-generated method stub
		System.out.println(t);
	}
} 

//使用extends关键字指定这个类型必须是继承某个类
class Animal
{
	
}
 
class Dog extends Animal
{
	
}
 
abstract class Cls1<T extends Animal>
{
	T a;
	
	public Cls1(T a){
		this.a = a;
	}
	
	public T getData(){
		return a;
	}
	
	abstract void printInfo();
}
 
interface Cls3<T3>
{
	abstract void printInfoCls3(T3 t);
}
 
class Cls2<T extends Animal,T2,T3> extends Cls1<T> implements Cls3<T3>
{
	T2 b;
	
	public Cls2(T a,T2 b){
		super(a);//调用了父类的构造方法,这个语句必须放在子类构造方法的第一句
		this.b = b;
	}
	
	public T2 getData2(){
		return b;
	}
 
	@Override
	void printInfo() {
		// TODO Auto-generated method stub
		System.out.println("print");
	}
 
	@Override
	public void printInfoCls3(T3 t) {
		// TODO Auto-generated method stub
		System.out.println(t);
	}
} 

//使用extends关键字指定这个类型必须是继承某个类,或者实现某个接口。
 
interface Move
{
	abstract void test();
}
 
class A implements Move//正常继承实现接口的方法使用的是implements
{
	public void test(){
		
	}
}
 
abstract class Cls1<T extends Move>//但是在泛型这里需要使用extends限定
{
	T a;
	
	public Cls1(T a){
		this.a = a;
	}
	
	public T getData(){
		return a;
	}
	
	abstract void printInfo();
}
 
interface Cls3<T3>
{
	abstract void printInfoCls3(T3 t);
}
 
class Cls2<T extends Move,T2,T3> extends Cls1<T> implements Cls3<T3>
{
	T2 b;
	
	public Cls2(T a,T2 b){
		super(a);//调用了父类的构造方法,这个语句必须放在子类构造方法的第一句
		this.b = b;
	}
	
	public T2 getData2(){
		return b;
	}
 
	@Override
	void printInfo() {
		// TODO Auto-generated method stub
		System.out.println("print");
	}
 
	@Override
	public void printInfoCls3(T3 t) {
		// TODO Auto-generated method stub
		System.out.println(t);
	}
} 

以上这些实际编程时很少用,现在是用来科普我们的认知,看到要认识。

后续编程安卓可能会用到。 

四、类型通配的方式

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

Cls1<Integer> c1 = new Cls1<Integer>();
Cls1<String> c2 = new Cls1<String>();
c1 = c2;//发生编译错误
 
Cls1<Object> c3 = c1;//c3和c1类型并不兼容,发生编译错误
c3 = c2;//c3和c2类型同样不兼容,也会发生编译错误

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

类型通配声明 例子 

类型通配的方式 

  • "?"代表任意一个类型 

class Animal
{
	
}
 
class Dog extends Animal
{
	
}
 
 
class Cls1<T>
{
	T a;
	
	public Cls1(T a){
		this.a = a;
	}
	
	public T getData(){
		return a;
	}
} 
 
public class Test {
	public static void main(String[] args) {
		Cls1<Integer> c1 = new Cls1<Integer>(10);
		Cls1<Double> c2 = new Cls1<Double>(10.0);
		Cls1<?> c3;
		
		c3 = c1;
		c3 = c2;
	}
}
  • 和限制泛型的上限相似,同样可以使用extends关键字限定通配符匹配类型的上限 
class Animal
{
	
}
 
class Dog extends Animal
{
	
}
 
 
class Cls1<T>
{
	T a;
	
	public Cls1(T a){
		this.a = a;
	}
	
	public T getData(){
		return a;
	}
} 
 
public class Test {
	public static void main(String[] args) {
		Cls1<Dog> c1 = new Cls1<Dog>(new Dog());
		Cls1<? extends Animal> c2;
		
		c2 = c1;
	}
}
  • 还可以使用super关键词限定通配符匹配类型的下限

class Animal
{
	
}
 
class Dog extends Animal
{
	
}
 
 
class Cls1<T>
{
	T a;
	
	public Cls1(T a){
		this.a = a;
	}
	
	public T getData(){
		return a;
	}
} 
 
public class Test {
	public static void main(String[] args) {
		Cls1<Dog> c1 = new Cls1<Dog>(new Dog());
		Cls1<? super Dog> c2;
		//Cls1<Animal> c1 = new Cls1<Animal>(new Dog());
		//Cls1<? super Dog> c2;
		c2 = c1;
	}
}

五、泛型方法 

  • 不仅类可以声明泛型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法。其定义格式为:

访问修饰符 <泛型列表> 返回类型 方法名(参数列表){
    实现代码
}
  • 在泛型列表中声明的泛型,可用于该方法的返回类型声明、参数类型声明和方法代码中的局部变量的类型声明。

  • 类中的其他方法不能使用当前方法声明的泛型。

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

class A<T>
{
	public void printInfo(T t){
		System.out.println(t);
	}
}
 
class B
{
	public <T> void printInfo(T t){
		System.out.println(t);
	}
}
 
public class Test {
	public static void main(String[] args) {
		A<String> a = new A<String>();
		a.printInfo("ajie");//此泛型中的方法参数类型被类的泛型类型限制住
		
		B b = new B();
		b.printInfo("ajiezaixian");//泛型方法不应该被类的泛型类型限制住
		b.printInfo(1234);
		b.printInfo(0.5);
	}
}

运行结果:

ajie
ajiezaixian
1234
0.5 

class A<T>
{
	public void printInfo(T t){
		System.out.println(t);
	}
}
 
class B
{
	//泛型方法
	public <T> void printInfo(T t){
		System.out.println(t);
	}
	//泛型方法的重载
	public <T,T2> void printInfo(T t,T2 t2){
		System.out.println(t);
		System.out.println(t2);
		//System.out.println(t+t2);会报错 只有实例化确定类型后才让加 跟我们之前遇到的要区分开
	}
}
 
public class Test {
	public static void main(String[] args) {
		A<String> a = new A<String>();
		a.printInfo("ajie");//此泛型中的方法参数类型被类的泛型类型限制住
		
		B b = new B();
		b.printInfo("ajiezaixian");//泛型方法不应该被类的泛型类型限制住
		b.printInfo(1234);
		b.printInfo(0.5);//比方法重载好用多了
		
		b.printInfo("ajie", "这是泛型方法的重载");
	}
}

运行结果

 ajie
ajiezaixian
1234
0.5
ajie
这是泛型方法的重载

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

添加类型约束只作用于一个方法的多个参数之间、而不涉及到类中的其他方法时。

施加类型约束的方法为静态方法,只能将其定义为泛型方法,因为静态方法不能使用其所在类的类型参数。 

class Animal
{
	public void eat(){
		System.out.println("动物吃");
	}
}
 
class Dog extends Animal
{
	public void eat(){
		System.out.println("啃骨头");
	}
}
 
class Cat extends Animal
{
	public void eat(){
		System.out.println("吃鱼肉");
	}
}
 
class B
{
	//泛型方法
	public <T> void printInfo(T t){
		System.out.println(t);
	}
	
	public static<T extends Animal> void printInfo(T t){
		t.eat();
	}
}
 
public class Test {
	public static void main(String[] args) {		
		B b = new B();		
		b.printInfo(new Dog());
		b.printInfo(new Cat());
		b.printInfo(new Animal());
		B.printInfo(new Dog());
	}
}

运行结果:

啃骨头
吃鱼肉
动物吃
啃骨头