java 进阶--泛型

1.什么是泛型?

在定义类时,不会给 类中成员: ( 属性 方法的返回值 方法的参数 )定义数据类型,而在类对象创建时为其指定相应的数据类型

泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。

泛型的格式:<数据类型>

常见的泛型-------集合中的泛型 :  List <E>

注意:泛型只能支持引用数据类型

2.为什么使用泛型

举个栗子:定义一个姓名 和年龄

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<String> list = new ArrayList<>();

        //2.添加数据
        //list.add(113);
        list.add("小张");
        //list.add(new student("小丽",18));

        //3.遍历集合获取每一个元素
        Iterator<String> 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

3.如何定义泛型类?

泛型可以在很多地方进行定义

写在类后面称为泛型类,写在方法上面称为泛型方法,写在接口后面称为泛型接口

泛型类使用场景:当一个类中.某个变量的数据类型不确定时,就可以定义带有泛型的类

格式:

修饰符 class 类名<类型>{

}

举个栗子:这里的<E>就表示不确定的类型,当创建该类对象时,E就确定了类型,此处E可以理解为变量,但是不是用来记录数据的,而是记录数据的类型,可以写成任意的:T丶E丶K丶V等

public class ArrayList <E> {

}

package com.demo.demo1;

import java.util.Arrays;

/*
*
*    当我在编写一个类的时候,如果不确定类型,那么这个类就可以定义为泛型类
* */
public class MyArrayList <E>{
    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<String> list=new MyArrayList<>();

    }
}

 只能添加字符串,形参变成了字符串

 放入Integer类型就会报错

 

 

 一样的效果

4.如何定义泛型方法?

方法中形参类型不确定时.可以使用类后面定义的泛型<E>

如果只有类中只有一个方法不确定,就没必要在类上加上泛型,只需要在方法上加上泛型,称为泛型方法,但是其他的方法就不能用了,想用也加上与其对应的<E>

如果方法形参不确定有两种方案:

方案一:使用类后面定义的泛型, 所有方法都能用

方案二:在方法申明上定义自己的泛型,只有本方法能用

格式:

修饰符<类型>返回值类型 方法名(类型 变量名){

}

举例:此处T可以理解为变量,但是不是用来记录数据的,而是记录类型的,可以写成:T、E、K、V等,调用该方法时,T就确定类型

public <T> void show ( T t ){

}

泛型方法的简单定义

[访问权限] <泛型标识> 泛型标识 方法名称(泛型标识 参数名称){

}

举个栗子:

定义一个工具类:ListUtil

类中定义一个静态方法allAll,用来添加多个集合的元素

package com.demo.demo1;

import java.util.ArrayList;

public class ListUtil {
    private ListUtil(){}
    //类中定义一个静态方法allAll,用来添加多个集合的元素

    /*
    *
    *   参数一:集合
    *   参数二~最后:要添加的元素
    * */
    public  static<E> void addAll(ArrayList<E>list,E e1,E e2,E e3){
      list.add(e1);
      list.add(e2);
      list.add(e3);
    }
    public void show(){
        System.out.println("哦吼");
    }
}

  方法被调用,类型就能确认下来

 Integer类型

想一直添加又不确定长度可以用可变参数

 public  static<E> void addAll(ArrayList<E>list,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<String> list = new ArrayList<>();

        ListUtil.addAll(list, "aaa", "bbb", "ccc");

        System.out.println(list);

        ArrayList<Integer> list2 = new ArrayList<>();
        ListUtil.addAll(list2,1,2,3);
        System.out.println(list2);


        ArrayList<Integer> 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);
    }
}

小细节:

泛型方法的定义与其所在的类是否是泛型类是没有任何关系的,所在的类可以是泛型类,也可以不是泛型类。

5.如何定义泛型接口

当一个接口中类型不确定可以使用泛型接口

泛型接口的两种使用方式:

方案一:使用类后给出具体类型

方案二:实线类持续泛型,创建对象时再确定

重点:如何使用一个带泛型的接口

格式:

修饰符 interfacce 接口名 <类型>{

}

举例:也是不确定类型用一个<E>

public interface List<E>{

}

方案一:给出具体类型

创建一个接口

 

调用add方法

给其他类型就会报错

 

 方案二:实线类持续泛型,创建对象时再确定

创建不确定接口

 

创建的时候必须给其类型

 

package com.demo.demo1;

public class Demo4 {
    public static void main(String[] args) {
        /*
         泛型接口的两种使用方法:
                1.实现类给出具体的类型
                2.实现类延续泛型,创建实现类对象时再确定类型
         * */

        MyArrayList2 list=new MyArrayList2();
        list.add("123");

        MyArrayList3<String>list2=new MyArrayList3<>();
        list2.add("123");
    }
}

结论:当你确定类型的时候,可以使用第一种,当你不确定的时候可以使用第二种

注意:第二种调用的时候必须给其类型

6.泛型的继承和通配符

泛型不具备继承性,但是数据具备继承性

 泛型里面写的是什么类型,那么只能传递什么类型的数据

package com.demo.demo1;

import java.util.ArrayList;

public class Demo5 {
    public static void main(String[] args) {
        /*
        *   泛型不具备继承性,但是数据具有继承性
        * */

        //创建集合的对象
        ArrayList<Ye>list1=new ArrayList<>();
        ArrayList<Fu>list2=new ArrayList<>();
        ArrayList<Zi>list3=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<Ye> 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) {
        /*
        *   需求:
        *        定义一个方法,形参是一个集合,但是集合中的数据类型不确定。
        * */

        //创建集合的对象
        ArrayList<a>list1=new ArrayList<>();
        ArrayList<b>list2=new ArrayList<>();
        ArrayList<c>list3=new ArrayList<>();
        ArrayList<Student>list4=new ArrayList<Student>();


        method(list1);
        method(list2);
        method(list3);
        method(list4);
    }

    /*
     * 此时,泛型里面写的是什么类型,那么只能传递什么类型的数据
     *
     * 弊端:
     *      利用泛型方法有一个小弊端,此时他还可以接受任意的数据类型
     *      Ye Fu  zi Student
     *
     * 希望:本方法虽然不确定类型,但是以后我希望只能传递a b c
     * */
    public  static<E>  void method(ArrayList<E> 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.如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以泛型的通配符泛型的通配符:
关键点:可以限定类型的范围。

不想限定就可以用之前的泛型类,泛型方法,泛型接口就可以了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值