java基础 浅谈 java 泛型

泛型:是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。

Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

其实前面见过很多,只是在使用的时候有时候会忽略的,比如前面讲的集合,其中List ,其实这个list 就是一个泛型的应用。而具体有什么用呢?先看一下代码简单的理解一下。

public class Test {
   public static void main(String[] args) {
       //这个没有<> 默认就是Object类型,所以可以放任何数据
	  List list1=new ArrayList();
	  list1.add(1);
	  list1.add("String");
	  System.out.println(list1);// [1, String]
	  
	  List<String> list2=new ArrayList<String>();
	  list2.add("String");
//	  如果添加一个非<>中定义的数据,就会在编译的时候报错,比如下面这个就会报错只能添加一个String类型的数据
//	  list2.add(1);//The method add(int, String) in the type List is not applicable for the arguments (int)
   }
}

这个地方就有一个疑问了,既然就是放数据,不写泛型也可以放数据,而且还不限制类型,那干嘛弄一个泛型,岂不是画蛇添足了?

非也,其实这个涉及到了如果不定义泛型,直接用Object的时候,比如放了自己定义的一个类,调用时候需要强制转换。如果放十几个不同的类型的自定义对象,那需要判断然后再强制转换,其实让代码有点累赘。还有比如打算存储用户的年龄,如果使用Object,那在放入数据的时候,放要一个字符串也不会报错,我们比较年纪的时候就会报错。

泛型的作用:

    • 代码更加简洁,不用使用强制转换。代码可读性更高,看着简洁。
    • 程序会更加健壮,只要编译无错一般在运行的时候也不会报错。

    注意:泛型只能是数据类型,无法使用基本类型。还有在<>中jdk源码用大写E,是英语的缩写,提示这个写成其他英语字母也完全无问题,可以看下面自定义的时候用T

这个就不在举例了,因为类型定了,常见的集合中进行循环取数据的时候会感觉出来。所以不在多举例。

自定义泛型类

  • 定义一个基本的泛型类(其实可以多个泛型,但是目前用一个进行)
public class testA<T> {
	//为了省事,我直接在自建泛型类中调用了
	public static void main(String[] args) {
//		testA testa=new testA(); 或者testA testa=new testA(); 还有testA testa=new testA(); 都是错误的
//		正确的写法创建对象如下:
		testA testa=new testA();//这里没有对T进行定义,所以默认就是T 的数据类型是Object类型
		//可以正常调用其方法。
		testa.setName("xiaoming");
//		建议:如果调用泛型类的时候,最好使用泛型类的有参构造方法,这样在创建对象的时候就定义好T的具体类型,这样可以避免很多异常
		testA testa1=new testA("xiaoming",123, "test");
//		System.out.println(testa.getT());
	}
	String name;
	int age;
	T t;
	 //无参的构造方法
    public testA() {   
   }
// 这个地方要注意,虽然是自定义的泛型类,其构建方法不用添加一对<>括号
	public testA(String name, int age, T t) {
		super();
		this.name = name;
		this.age = age;
		this.t = t;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public T getT() {
		return t;
	}
	public void setT(T t) {
		this.t = t;
	}


}

而基本的泛型类是否可以继承?

答案是可以,用代码来看。

子类继承分两种

  • 保持子类的泛型化
  • 子类不再泛型化
保持子类的泛型化

这种其实有的限制会偏多一些,自然格式也会有些限制。

//不用java中常用数据类,而新建一个类,因为会涉及到继承
public class Temp {
    }
//定义一个基本的泛型类
 public class ClassA<T>{
     
    }
 //继承泛型类后的子类依然是一个泛型类  
   public class ClassB<T> extends ClassA{//不写意味着也是泛型是Object
    }

    public class ClassB<T> extends ClassA<T>{
    }
//缩小泛型的范围,是准许的,但是不允许扩大泛型的范围。因为T的范围可以说继承Object ,而Object是所有类的父类或者间接父类。
    public class ClassC<T extends Temp> extends ClassA<T>{
   
    }
 //既然子类泛型可以继承父类,泛型父类自然也可以
  //具有泛型的类ClassD,泛型具有父类约束
    public class ClassD<T extends Temp> {

    }
    //继承ClassD,声明泛型时至少具有父类同样的约束
    public class ClassE<T extends Temp> extends ClassD<T>{
  
    }


//如果泛型的子类,那样再创建子类的时候需要给子类的泛型赋予一个类,这样才能体现泛型的优点,如果不添加那就是默认Object
ClassB Classb=new ClassB<Temp>();

总结:

  • 保持类的泛型化,其中此类的泛型只能是其父类泛型或者接口泛型的范围内,而而不能扩大父类或接口的泛型范围。
子类不再泛型化.
//不用java中常用数据类,而新建一个类,因为会涉及到继承
public class Temp {
    }
//定义一个基本的泛型类
 public class ClassA<T>{
  
  //第一种   
 public class ClassB extends ClassA<Temp>{

}    
    //第二种 
public class ClassB<Temp> extends ClassA<Temp>{

}
     
 //以上两种写法都对,但是一般创建子类都用第一种
  
//那么在创建子类对象的时候,就不用写泛型类,因为默认就是Temp了,可以体现泛型的优点了
     ClassB Classb=new ClassB();
     

补充:

无论再泛型类还是泛型接口中,都可以作为非静态属性的类型,非静态方法的返回值类型。但是再静态方法中不能使用类的泛型。还有异常类也不可以使用泛型。

泛型接口

既然有泛型类,自然也就有泛型接口。而且泛型接口的限制和泛型类几乎一样,只是不能new 接口,只能通过new子类进行实例化罢了。

看代码即可,其实再使用集合类的时候,List其实就是一个典型的泛型接口。

public interface ClassA<T1,T2> {

}

其实实现接口类的要求和继承泛型父类的子类差不多,但举例用双泛型其实也算是扩充了。不过其注意细节和泛型类差不多,所以不再用代码演示。

泛型方法

对于泛型方法,很多人第一反应,这个还需要单独拿出来说吗?刚才泛型类中中不是有带泛型的方法吗?

泛型类中是带有泛型的方法,但是其不是我说的泛型方法,因为其用的泛型是类带的泛型,而不是这个方法独有的泛型。只有某个方法带有泛型与泛型类带的泛型定义的字符不一样才是泛型方法。老规矩看代码:

package test;

import java.util.List;

public class testA<T> {


	String name;
	int age;
	T t;
	//无参的构造方法
	public testA() {   
	}
	// 这个地方要注意,虽然是自定义的泛型类,其构建方法不用添加一对<>括号
	public testA(String name, int age, T t) {
		super();
		this.name = name;
		this.age = age;
		this.t = t;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public T getT() {
		return t;
	}
	public void setT(T t) {
		this.t = t;
	}

	
//	 泛型方法
	public <E > void show(E e) {// 无论是否有返回值,需要再方法中加入 其作用是声明E 不是一个类名,而是一个泛型。 如果和泛型类中的泛型字符一样就不需要声明,因为类加载的时候已经告诉编译器这个是泛型
		
	}
//	 泛型方法
	public <E > void show(List<E> list) {//无论是否有返回值,需要再方法中加入 其作用是声明E 不是一个类名,而是一个泛型。 如果和泛型类中的泛型字符一样就不需要声明,因为类加载的时候已经告诉编译器这个是泛型
		
	}
	
	
//	 泛型方法
	public <E > E  play(E e) {// 无论是否有返回值,需要再方法中加入 其作用是声明E 不是一个类名,而是一个泛型。 如果和泛型类中的泛型字符一样就不需要声明,因为类加载的时候已经告诉编译器这个是泛型
		return e;
	}
//	 泛型方法
	public <E > List<E> play(List<E> list) {// 无论是否有返回值,需要再方法中加入 其作用是声明E 不是一个类名,而是一个泛型。 如果和泛型类中的泛型字符一样就不需要声明,因为类加载的时候已经告诉编译器这个是泛型
		return list;
	}
	
	//泛型方法可以使用static 方法,因为泛型方法 在类调用的时候才会赋值所以不会报错
	public static <E> void StaticShow(E e) {
		
	}
//	 那怕泛型类一样,写法和泛型方法一样写也无法使用静态,也会报错
    public static <T> void StaticShow2(T t) {
		System.out.println(t);
	}
    
    //这个可以看出如果StaticShow2 按照StaticShow方法进行写,那样的话泛型方法T不会受到泛型类T的约束。而其实泛型方法也可以用T进行演示,但是为了好理解所以用E来演示, 
	public static void main(String[] args) {
		testA<Integer> t =new testA<Integer>("xiaoming", 1, 1);
		t.StaticShow2("dfdf".getClass());
//		t.setT("er"); 这个地方会报错格式不一样 The method setT(Integer) in the type testA is not applicable for the arguments (String)
	}
}

总结:

  • 泛型方法中的泛型如果在泛型类中,其中两者的泛型不会被彼此影响,那怕使用了相同的泛型字母。前面为什么说使用不同的泛型字母只是为了方便理解而已。
  • 泛型方法可以使用静态关键字static来修饰,同时在写方法的时候 无论是否会有返回值 记得添加 来声明此字母是 泛型 例如:public void show(E e)

补充

补充泛型类的另类误解继承

来一个补充,先看代码,然后再进行总结。

public class Test{
public static void main(String[] args) {
	List list1=new ArrayList();
	list1.add(1);
	list1.add(2);
	List list2=new ArrayList();
	list2.add("String1");
	list2.add("String2");
//	如果不写泛型的种类,直接添加数据,然后讲list赋值给list1编译通过
	System.out.println(list1);//[1, 2]
	list1=list2;
	System.out.println(list1);//[String1, String2]
	

	List<Integer> list3=new ArrayList<Integer>();
	list3.add(1);
	list3.add(2);
	
	List<String> list4=new ArrayList<String>();
	list4.add("String1");
	list4.add("String2");
	System.out.println(list3);
	//不指定泛型的list可以转换成任意泛型类型的集合,而不是因为无泛型默认值得是object,所以可以。下面举例你会发现不对
	list3=list2;
	System.out.println(list3);
	
    
    List<Object> list5=new ArrayList();
	list5.add("String1");
	list5.add("String2");
	//list3=list5; // Type mismatch: cannot convert from List to List 不能用Object集合赋值给Integer集合
	//list5=list3;//反过来也会报错
    
    
	
	// 虽然前面list3=list2; list3存储了string,但是也不能直接将list4赋值给list3,编译不会通过的,会报错。
//	list3=list4;// 报错: ype mismatch: cannot convert from List to List  不能用string集合赋值给Integer集合。
	
	
}
}
 
  

总结:

​ 两个泛型集合如果泛型赋值,两者不可以相互赋值。那怕其中一个是Object对泛型进行赋值的,因为泛型有继承关系不会影响那怕同一个类(例如list),其是并列关系,没有一点继承关系。如果list不指定泛型,可以转换成任意类型的list。

​ 但是如果父类 father ,与son 。son是father的子类,而且G的泛型类一样,那样两者可以有继承关系。

通配符

对于通配符,就是意如字面的意思,就是在泛型中通过通配符占一个位置而已,具体看代码

public class Test {
public static void main(String[] args) {
	List<Integer> list1=new ArrayList<Integer>();
	list1.add(1);
	list1.add(2);

	List<String> list2=new ArrayList<String> ();
	list2.add("String1");
	list2.add("String1");
	
	List<?> list3=new  ArrayList<>();//其中ArrayList<>() 中的<> 可带可不带,但是不能写会报错,因为具体实现类中没有?这个类
	list3=list1;
	list3=list2;
	
}
}

可以看出如果用通配符 ? 就可以被其他泛型集合赋值了,和前面说的异类继承完全冲突了。这个需要注意一点通配符可以理解为一个占位符,因为通配可以理解为可以匹配任何类型的泛型类。但是其也有一些其他的要注意的事项。先看代码然后再解释:

public class test {
	public static void main(String[] args) {


		List<?> list=new  ArrayList<>();


		//可以看出通配符只能占位,但是无法进行添加数据(null除外)
		//list.add("dd");//The method add(capture#3-of ?) in the type List is not applicable for the arguments (String)
		//list.add(1);The method add(int, capture#1-of ?) in the type List is not applicable for the arguments (int)


		//但是使用通配符的,唯一可以添加的数据是null 因为任何数据类都可以为null。
		list.add(null);


		List<Integer> list1=new ArrayList<Integer>();
		list1.add(1);
		list1.add(2);

		list=list1;
		//通过用其他泛型类的集合赋值后,可以取出数据,不过只要警告其处理后都变得object类型了
		System.out.println(list.get(0));
		printList(list1);
	}

	public static void printList(List<?> list) {
		//可以看出 通配符 取出的数据是Object
//		Iterator iterator=(Iterator) list.iterator();
		Iterator iterator= list.iterator();
		while(iterator.hasNext()) {
			Object o=iterator.next();
			System.out.println(o);
		}
	}

总结:

  • 通配符的集合是无法添加数据的,除非是null。
  • 通过通配符取出的数据,默认是object类型

有限制的通配符,这个先不说定义是什么我们先看代码。

package test;

import java.util.ArrayList;
import java.util.List;

public class testB{

	public static void main(String[] args) {
		List<? extends Father> list=null;
		List<? super Father> list1=null;
	
//		Father father=new Father();
//		FirstSon firstSon=new FirstSon();
		
		List<Father> listFather=null;
		List<FirstSon> listFirstSon=null;
		List<Object> listObject=null;
		
		list=listFather;
		
		list=listFirstSon;
//		List 的集合只能为  泛型Father的自己或者子类 所以下面会报错
//		list=listObject;//Type mismatch: cannot convert from List to List
		
		
//		List list 在取出数据的时候默认其为Father
		Father father=list.get(0);
//		FirstSon firstSon=(FirstSon) list.get(0);
//		同样受通配符限制无法添加数据
//		list.add(new Father());//The method add(capture#4-of ? extends Father) in the type List is not applicable for the arguments (Father)
		

		list1=listFather;
//		List 的集合只能为  泛型Father的自己或者父类类 所以下面会报错
//		list1=listFirstSon;//Type mismatch: cannot convert from List to List
		list1=listObject;
	
	
//		List list1 在取出数据的时候默认其为Object
		Object object= list1.get(0);
//		Father father1=(Father) list1.get(0);
//		同样受通配符限制无法添加数据
//		list.add(new Object());//The method add(capture#7-of ? extends Father) in the type List is not applicable for the arguments (Object)
	
	}
	
}

 
  

                            
                        
                    
                    
                    

你可能感兴趣的:(Java基础,java,编程语言)