1.泛型类:在定义类的时候,在类名的后面加上。
1)E只是起到占位的作用,当创建对象时,赋予真正的类型。
2)E是泛型的形参,可以是一个,也可以是多个,如果是多个,用逗号隔开。
3)泛型形参的命名规则,只要符合java的标识符规范就可以。但是,通常泛型形参是一个大写字母。如:E(element),K(key),V(value)。
4)E可以再整个类中使用。比如:类的属性,类的方法参数,类的方法返回值。
5)在创建对象的时候,泛型类型,左边一定要写,右边可写可不写。如果右边写了,左边没写,跟没写一样。
FanXingClass<String> fanXingClass=new FanXingClass<>();
public class FanXingClass<E> {
//E作为属性类型
private E e;
//E作为返回值类型
public E getE() {
return e;
}
//E作为参数类型
public void setE(E e) {
this.e = e;
}
public static void main(String[] args) {
//E为String类型
FanXingClass<String> fanXingClass=new FanXingClass<>();
fanXingClass.setE("hahaha");
System.out.println(fanXingClass.getE());
//E为Integer类型
FanXingClass<Integer> fanXingClass2=new FanXingClass<>();
fanXingClass2.setE(111);
System.out.println(fanXingClass2.getE());
//E为Float类型
FanXingClass<Float> fanXingClass3=new FanXingClass<>();
fanXingClass3.setE(3.1415F);
System.out.println(fanXingClass3.getE());
}
}
2.泛型接口:在定义接口的时候,在接口名的后面加上。
关于泛型接口的实现,有两种方式:
1)如果接口后面还是泛型,那么前面的类也必须是泛型。在创建对象时,再将类型传进去。
2)如果接口后面是具体的类型,那么前面的类中不用写<>。在创建对象时,不需要传类型了,因为类型在接口中已经确定了。
接口类:FanXingInterfaceDemo
public interface FanXingInterfaceDemo<E> {
//E e; 接口中的属性默认是public static final修饰的,是常量,而 E 泛型是不确定的。所以不能再接口中定义泛型变量。
public void test1(E e);
public E test2();
}
1)如果接口后面还是泛型,那么前面的类也必须是泛型。
public class FanXingInterfaceSub<E> implements FanXingInterfaceDemo<E>
public class FanXingInterfaceSub<E> implements FanXingInterfaceDemo<E> {
private E e;
public void setE(E e){
this.e=e;
}
public E getE(){
return e;
}
@Override
public void test1(E e) {
System.out.println(e);
}
@Override
public E test2() {
return e;
}
public static void main(String[] args) {
FanXingInterfaceSub<String> fxis1=new FanXingInterfaceSub<>();
fxis1.setE("abc");
fxis1.test1(fxis1.getE());
System.out.println(fxis1.test2());
FanXingInterfaceSub<Integer> fxis2=new FanXingInterfaceSub<>();
fxis2.setE(111);
fxis2.test1(fxis2.getE());
System.out.println(fxis2.test2());
}
}
------------------------------------------------------------------------
输出结果:
abc
abc
111
111
2)如果接口后面是具体的类型,那么前面的类中不用写<>。
public class FanXingInterfaceSub2 implements FanXingInterfaceDemo<String>
public class FanXingInterfaceSub2 implements FanXingInterfaceDemo<String> {
@Override
public void test1(String s) {
System.out.println(s);
}
@Override
public String test2() {
return "test2";
}
public static void main(String[] args) {
FanXingInterfaceSub2 fxis1=new FanXingInterfaceSub2();
fxis1.test1("abc");
System.out.println(fxis1.test2());
}
}
3.泛型方法
在方法的返回值前面定义泛型,然后在方法中使用泛型。
public <E> void show(E e){
e);
}
public class FangXIngMethod {
public <E> void show(E e){
System.out.println(e);
}
public static void main(String[] args) {
FangXIngMethod fxm=new FangXIngMethod();
fxm.show(123);
fxm.show("abc");
}
}
4.泛型的上限:
只定义在方法中(工作中不用,查找别人的API,会有)
形式:<? extends 父类>
意义:如果父类确定了,所有的子类都可以直接使用。参数类型只能是该类型或该类型的子类
5.泛型的下限:
只定义在方法中
形式:<? super 子类>
意义:如果子类确定了,子类的所有的父类都可以直接传递参数值。参数类型只能是该类型或该类型的父类。
代码示例:
public class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Student extends Person {
private String classRoom;
public Student(String name, int age, String classRoom) {
super(name, age);
this.classRoom = classRoom;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", classRoom=" + classRoom + '\'' +
"} " ;
}
}
public class Test {
public static void method1(ArrayList<? extends Person> list){
for(Person p:list){
System.out.println(p);
}
}
public static void method2(ArrayList<? super Student> list){
for(Object o:list){
System.out.println(o);
}
}
public static void main(String[] args) {
ArrayList<Person> list1=new ArrayList<>();
list1.add(new Person("a",11));
list1.add(new Person("b",12));
list1.add(new Student("c",13,"classRoom1"));
System.out.println("method1_<? extends Person>_list1:");
method1(list1);
System.out.println("----------------------------------");
ArrayList<Student> list2=new ArrayList<>();
list2.add(new Student("d",14,"classroom2"));
list2.add(new Student("e",15,"classroom2"));
System.out.println("method1_<? extends Person>_list2:");
method1(list2);
System.out.println("----------------------------------");
ArrayList<Student> list3=new ArrayList<>();
list3.add(new Student("f",14,"classRoom3"));
list3.add(new Student("g",15,"classroom3"));
System.out.println("method2_<? super Student>_list3:");
method2(list3);
System.out.println("------------------");
System.out.println("method2_<? super Student>_list4:");
ArrayList<Person> list4=new ArrayList<>();
list4.add(new Person("h",14));
list4.add(new Person("i",15));
method2(list4);
}
}
--------------------------------------------------
输出结果:
method1_<? extends Person>_list1:
Person{name='a', age=11}
Person{name='b', age=12}
Student{name='c', age=13, classRoom=classRoom1'}
----------------------------------
method1_<? extends Person>_list2:
Student{name='d', age=14, classRoom=classroom2'}
Student{name='e', age=15, classRoom=classroom2'}
----------------------------------
method2_<? super Student>_list3:
Student{name='f', age=14, classRoom=classRoom3'}
Student{name='g', age=15, classRoom=classroom3'}
------------------
method2_<? super Student>_list4:
Person{name='h', age=14}
Person{name='i', age=15}
总结:
1.泛型分为:泛型类,泛型接口,泛型方法。
2.泛型类是在类名后面加上<E>,E起到一个占位符的作用,相当于一个形参。当创建对象时,在确定具体参数类型。实现了解耦。E可以是类属性类型,方法参数类型,方法返回值类型。
3.泛型接口是在接口后面加上<E>,E起到一个占位符的作用,相当于一个形参。子类在实现接口的时候,有两种方式。方式一:接口中不是具体的类,还是泛型,则子类名称后面还要加上泛型。方式二:接口中用具体的类代替泛型,子类名称后面不用加上泛型。E可以是方法参数类型,方法返回值类型。但不能是属性类型。因为接口中的属性值都是常量,泛型不是常量。
4.泛型方法是在方法返回值前面声明泛型<E> ,E可以是方法参数类型,方法返回值类型。
5.泛型通配符有3种:<?>,<? extends E>,<? super E>.
6.<? extends E>:泛型的上限。只要是E或者E的子类都可以。只定义在方法中。
7.<? super E>:泛型的下限。只要是E或者E的父类都可以。只定义在方法中。
8.泛型的优点:数据安全;获取数据时,效率高。
9.定义泛型后,把一些异常由运行期提前到编译器。