在定义类时,不会给 类中成员: ( 属性 方法的返回值 方法的参数 )定义数据类型,而在类对象创建时为其指定相应的数据类型
泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
泛型的格式:<数据类型>
常见的泛型-------集合中的泛型 : List
注意:泛型只能支持引用数据类型
举个栗子:定义一个姓名 和年龄
package com.demo.demo1;
public class student {
private String name;
private int age;
public student() {
}
public student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "student{name = " + name + ", age = " + age + "}";
}
}
当添加时,没有给集合限制类型,所有默认是Object类型,因为Object是所有类的父类,
--自动向上转型
遍历出来取值
package com.demo.demo1;
import java.util.ArrayList;
import java.util.Iterator;
public class Demo {
public static void main(String[] args) {
//没有泛型的时候,集合如何储存数据
//1.创建集合的对象
ArrayList list= new ArrayList<>();
//2.添加数据
list.add(113);
list.add("小张");
list.add(new student("小丽",18));
//3.遍历集合获取每一个元素
Iterator it = list.iterator();
while (it.hasNext()){
Object obj = it.next();
//多态的弊端是不能访问子类的特有功能
obj.length();
System.out.println(obj);
}
}
}
多态的弊端是不能使用子类的特有功能,强行使用会报错
package com.demo.demo1;
import java.util.ArrayList;
import java.util.Iterator;
public class Demo {
public static void main(String[] args) {
//没有泛型的时候,集合如何储存数据
//1.创建集合的对象
ArrayList list= new ArrayList<>();
//2.添加数据
list.add(113);
list.add("小张");
list.add(new student("小丽",18));
//3.遍历集合获取每一个元素
Iterator it = list.iterator();
while (it.hasNext()){
String str = (String) it.next();
//多态的弊端是不能访问子类的特有功能
//obj.length();
str.length();
System.out.println(str);
}
}
}
而强转后也会报错,类型转换异常,Integer 不能强转为String类型 数据类型不安全问题
而强转为student 对象 字符和整数也都不能用了
集合加上String类型,也会报错
添加的数据要么全是String类型,要么全是Integer类型
package com.demo.demo1;
import java.util.ArrayList;
import java.util.Iterator;
public class Demo {
public static void main(String[] args) {
//没有泛型的时候,集合如何储存数据
// 结论:
//如果我们没有给集合指定类型,默认认为所有的数据类型都是object类型
// 此时可以往集合添加任意的数据类型。
//带来一个坏处:我们在获取数据的时候,无法使用他的特有行为。
//1.创建集合的对象
ArrayList list = new ArrayList<>();
//2.添加数据
//list.add(113);
list.add("小张");
//list.add(new student("小丽",18));
//3.遍历集合获取每一个元素
Iterator it = list.iterator();//迭代器也有泛型 和集合是保持一致的
while (it.hasNext()) {
String str = it.next();//就不用强转了
//多态的弊端是不能访问子类的特有功能
//obj.length();
//str.length();
System.out.println(str);
}
}
}
结论:
如果我们没有给集合指定类型,默认认为所有的数据类型都是object类型
此时可以往集合添加任意的数据类型。
带来一个坏处:我们在获取数据的时候,无法使用他的特有行为。
添加的数据要么全是String类型,要么全是Integer类型...等类型
所以java推出了泛型,可以在添加数据的时候就可以把类型进行统一,而且我们在获取数据的时候,也省的强转了,非常的方便
好处:
统一数据类型
把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来
扩展知识:
java中的泛型是伪泛型,只在编译器有效,假如有一个泛型的集合是String类型,只要是String类型都可以添加集合当中,当数据添加的时候仅仅是在门口去检查是否符合String类型,符合就添加进去,当添加到里面的时候,集合还是会这些数据当成Object类型来处理,当往外获取的时候,底层会把这些Object按着泛型类型去强转为对应的类型,编写的时候存在真正泛型,编译成字节码文件的时候,泛型就会消失,在java中有个专业名字,叫做"泛型的擦除"
在老版本的时候是没有泛型的,在jdk5才出来的,在jdk5以前都是当做Object类型,非常的不方便,又不可能去改以前的屎山源码,在原本的代码不动之上,在门口加个限定,这样就解决了又不用修改代码,又统一了类型,两全其美,美滋滋
泛型的细节:
泛型中不能写基本数据类型,因为写了也没办法转成Object类型,比如int只能用它的父类Integer才能转成Object
指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型
如果不写泛型,类型默认是Object
泛型可以在很多地方进行定义
写在类后面称为泛型类,写在方法上面称为泛型方法,写在接口后面称为泛型接口
泛型类使用场景:当一个类中.某个变量的数据类型不确定时,就可以定义带有泛型的类
格式:
修饰符 class 类名<类型>{
}
举个栗子:这里的
public class ArrayList
}
package com.demo.demo1;
import java.util.Arrays;
/*
*
* 当我在编写一个类的时候,如果不确定类型,那么这个类就可以定义为泛型类
* */
public class MyArrayList {
Object[]obj=new Object[10];
int size;
/*
E: 表示是不确定的类型,该类型在类名后面已经定义过了.
e:形参的名字,变量名
* */
public boolean add(E e){
obj[size]=e;
size++;
return true;
}
public E get(int index){
return (E)obj[index];
}
@Override
public String toString(){
return Arrays.toString(obj);
}
}
package com.demo.demo1;
public class Demo2 {
public static void main(String[] args) {
MyArrayList list=new MyArrayList<>();
}
}
放入Integer类型就会报错
方法中形参类型不确定时.可以使用类后面定义的泛型
如果只有类中只有一个方法不确定,就没必要在类上加上泛型,只需要在方法上加上泛型,称为泛型方法,但是其他的方法就不能用了,想用也加上与其对应的
如果方法形参不确定有两种方案:
方案一:使用类后面定义的泛型, 所有方法都能用
方案二:在方法申明上定义自己的泛型,只有本方法能用
格式:
修饰符<类型>返回值类型 方法名(类型 变量名){
}
举例:此处T可以理解为变量,但是不是用来记录数据的,而是记录类型的,可以写成:T、E、K、V等,调用该方法时,T就确定类型
public
}
泛型方法的简单定义
[访问权限] <泛型标识> 泛型标识 方法名称(泛型标识 参数名称){
}
举个栗子:
定义一个工具类:ListUtil
类中定义一个静态方法allAll,用来添加多个集合的元素
package com.demo.demo1;
import java.util.ArrayList;
public class ListUtil {
private ListUtil(){}
//类中定义一个静态方法allAll,用来添加多个集合的元素
/*
*
* 参数一:集合
* 参数二~最后:要添加的元素
* */
public static void addAll(ArrayListlist,E e1,E e2,E e3){
list.add(e1);
list.add(e2);
list.add(e3);
}
public void show(){
System.out.println("哦吼");
}
}
方法被调用,类型就能确认下来
Integer类型
想一直添加又不确定长度可以用可变参数
public static void addAll(ArrayListlist,E ...e){
for (E element:e) {
list.add(element);
}
}
package com.demo.demo1;
import java.util.ArrayList;
/*
定义一个工具类:ListUtil
类中定义一个静态方法allAll,用来添加多个集合的元素
* */
public class Demo3 {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
ListUtil.addAll(list, "aaa", "bbb", "ccc");
System.out.println(list);
ArrayList list2 = new ArrayList<>();
ListUtil.addAll(list2,1,2,3);
System.out.println(list2);
ArrayList list3 = new ArrayList<>();
ListUtil.addAll(list3,1,2,3,4,5,5,5,5,5,,5,5,5,5,5,5,5);
System.out.println(list3);
}
}
小细节:
泛型方法的定义与其所在的类是否是泛型类是没有任何关系的,所在的类可以是泛型类,也可以不是泛型类。
当一个接口中类型不确定可以使用泛型接口
泛型接口的两种使用方式:
方案一:使用类后给出具体类型
方案二:实线类持续泛型,创建对象时再确定
重点:如何使用一个带泛型的接口
格式:
修饰符 interfacce 接口名 <类型>{
}
举例:也是不确定类型用一个
public interface List
}
方案一:给出具体类型
创建一个接口
调用add方法
给其他类型就会报错
方案二:实线类持续泛型,创建对象时再确定
创建不确定接口
创建的时候必须给其类型
package com.demo.demo1;
public class Demo4 {
public static void main(String[] args) {
/*
泛型接口的两种使用方法:
1.实现类给出具体的类型
2.实现类延续泛型,创建实现类对象时再确定类型
* */
MyArrayList2 list=new MyArrayList2();
list.add("123");
MyArrayList3list2=new MyArrayList3<>();
list2.add("123");
}
}
结论:当你确定类型的时候,可以使用第一种,当你不确定的时候可以使用第二种
注意:第二种调用的时候必须给其类型
泛型不具备继承性,但是数据具备继承性
泛型里面写的是什么类型,那么只能传递什么类型的数据
package com.demo.demo1;
import java.util.ArrayList;
public class Demo5 {
public static void main(String[] args) {
/*
* 泛型不具备继承性,但是数据具有继承性
* */
//创建集合的对象
ArrayListlist1=new ArrayList<>();
ArrayListlist2=new ArrayList<>();
ArrayListlist3=new ArrayList<>();
//调用method方法
method(list1);
// method(list2);
// method(list3);
list1.add(new Ye());
list1.add(new Fu());
list1.add(new Zi());
}
public static void method(ArrayList list){
}
}
/*
* 此时,泛型里面写的是什么类型,那么只能传递什么类型的数据
* */
class Ye{}
class Fu extends Ye{}
class Zi extends Fu{}
弊端:
利用泛型方法有一个小弊端,此时他还可以接受任意的数据类型
a b c Student
希望:本方法虽然不确定类型,但是以后我希望只能传递a b c
package com.demo.demo1;
import java.util.ArrayList;
public class Demo6 {
public static void main(String[] args) {
/*
* 需求:
* 定义一个方法,形参是一个集合,但是集合中的数据类型不确定。
* */
//创建集合的对象
ArrayListlist1=new ArrayList<>();
ArrayListlist2=new ArrayList<>();
ArrayListlist3=new ArrayList<>();
ArrayListlist4=new ArrayList();
method(list1);
method(list2);
method(list3);
method(list4);
}
/*
* 此时,泛型里面写的是什么类型,那么只能传递什么类型的数据
*
* 弊端:
* 利用泛型方法有一个小弊端,此时他还可以接受任意的数据类型
* Ye Fu zi Student
*
* 希望:本方法虽然不确定类型,但是以后我希望只能传递a b c
* */
public static void method(ArrayList list){
}
}
class a{}
class b extends a{}
class c extends b{}
class Student{}
此时我们就可以使用泛型的通配符
? 也表示不确定的类型
它可以进行类型的限定
? extends E: 表示可以传递E 或者 E所有的子类类型
? super E:表示可以传递E或者E所有的父类类型
[设置上限]
声明对象: 类名称 extends 类> 对象名称;
定义类: [访问权限] 类名称<泛型标识 extends 类>{}
[设置下限]
声明对象: 类名称 super 类> 对象名称;
定义类: [访问权限] 类名称<泛型标识 super 类>{}
? extends E: 表示可以传递E 或者 E所有的子类类型
? super E:表示可以传递E或者E所有的父类类型
结论:
1.如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。
2.如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以泛型的通配符泛型的通配符:
关键点:可以限定类型的范围。
不想限定就可以用之前的泛型类,泛型方法,泛型接口就可以了