Java学习记录之泛型

泛型:是一种未知的数据类型,当我们不知道使用什么数据类型的时候,可以使用泛型
泛型也可以看成是一个变量,用来接收数据类型
E e :Element元素
T t :Type类型

下面通过一张图更具体的解释一下泛型:
在这里插入图片描述

import java.util.ArrayList;
import java.util.Iterator;

public class Demo01Genneric{
   public static void main(String[] args){
      show02();
   }
   /*
     创建集合对象,使用泛型
     好处:
         1.避免类型转换的麻烦,存储的是什么类型,取出的就是什么类型
         2.把运行期异常(代码运行之后会抛出的异常),提升到了编译期(写代码的时候报错)
     弊端:
         泛型是什么类型,只能存储什么类型的数据
   */
   private static void show02(){
     ArrayList<String> list = new ArrayList<>();
     list.add("aaa");
     //list.add(1);报错,因为创建对象时,已经确定只能添加String类型的数据
     
      //使用迭代器遍历list集合
      Iterator<String> it = list.iterator();//iterator()方法在Collection集合中,ArrayList集合实现了Collection集合,所以可以直接调用里面的方法
      //使用迭代器中的方法hasNext和next遍历集合
      while(it.hasNext()){
          String s = it.next();
          System.out.println(s+"-->"+s.length);//aaa-->3  *.length可获取字符串的长度*
      }
   }
   
   /*
      创建集合对象,不使用泛型
      好处:
         结合不使用泛型,默认的类型就是Object类型,可以存储任意类型的数据
      弊端:
         不安全,会引发异常
   */
   private static void show01(){
      ArrayList list = new ArrayList();//不使用泛型
      list.add("abc");//存储字符串
      list.add(1);//存储整数
      //使用迭代器遍历list集合
      Iterator it = list.iterator();
      while(it.hasNext()){
        Object obj = it.next();
        System.out.println(obj);

        //在此之前,代码都不会报错,
        //但是想要使**用String类特有的方法,.length获取字符串的长度时**,不能使用  因为多态 Object obj = "abc"
        //**需要向下转型**
        String s = (String)obj;//但此时就会抛出ClassCastException类转换异常,因为集合中存在整数,不能把Integer类型转换为String类型
        
      }
      
   }
}

下面通过定义一个含有泛型的类,来模拟ArrayList集合

/*
  泛型是一个未知的数据类型,当我们不能确定什么类型的时候,可以使用泛型
  泛型可以接收任意的类型类型,可以使用Tnteger、String、Student...
  创建对象的时候确定泛型的数据类型
*/
//创建一个泛型类
public class GenernicClass{
   private E name;
   public E getName(){
     return name;
   }
   public void setName(E name){
      this.name = name;
   }
}

//主程序
public class Demo02GenernicClass{
   public static void main(String[] args){
      //**不写泛型默认为Object类型**
      GenernicClass gc = new GenernicClass();
      gc.setName("只能是字符串");
      Object obj = gc.getName();

      //创建GenernicClass对象,泛型使用Integer类型
      GenernicClass<Integer> gc1 = new GenernicClass<>();
      gc1.setName(1);
      Integer name = gc1.getName();
      System.out.println(name);//1

      //创建GenernicClass对象,泛型使用String类型
      GenernicClass<String> gc2 = new GenernicClass<>();
      gc2.setName("小明");
      String name = gc2.getName();
      System.out.println(name);//小明
   }
}

定义含有泛型的方法

/*
  定义含有泛型的方法:泛型定义在方法的*修饰符和返回值类型之间*
  格式:
     修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型)){
         方法体;
     } 
 含有泛型的方法,在调用方法的时候确定泛型的数据类型
 传递什么类型的参数,泛型就是什么类型   
*/
public class GenernicMethod{
   //定义一个含有泛型的方法
   public <E> void method01(M m){
      System.out.println(m);
   }
   //定义一个含有泛型的静态方法
   public static <S> void method02(S s){
       System.out.println(s);//表示泛型的字母,没有实际的区别
   }
}

//测试含有泛型的方法
public class Demo03GenernicMethod{
   public static void main(String[] args){
       //创建GenernicMethod对象
       GenernicMethod gm = new GenernicMethod();
       /*
         调用含有泛型的方法method01
         **传递什么类型,泛型就是什么类型**
       */
       gm.method01(10);
       gm.method01("abc");
       gm.method01(8.8);
       gm.method01(true);

       //使用静态方法
       GenernicMethod.method02("静态方法");
       GenernicMethod.method02(1);
   }  
}

定义含有泛型的接口

//定义格式:
      修饰符 interface 接口名<代表泛型的变量> {}
      
//含有泛型的接口
public interface GenernicInterface<I>{
    public abstract void method(I i);
}

/*
   含有泛型的接口,第一种使用方式:定义接口的实现类,实现接口,指定接口的泛型
   例如:1. public interface Iterator<E>{
               E next();
           }
    2.Scanner类实现了Iterator接口,并指定接口的泛型为String,所以重写next方法泛型默认就是String
  public final class Scanner implements Iterator<String>{
        public String next() {}
   }
*/
//用第一种方法创建实现类
public class GenernicInterfaceImpl1 implements GenernicInterface<String>{
   @Override
   public void method(String s){//String引用类型是跟着实现类确定的
       System.out.println(s);
   }
}
/*
   含有泛型的接口第二种使用方式:接口使用什么泛型,实现类就使用什么泛型,类跟着接口走
   就相当于定义了一个含有泛的类,创建对象的时候确定泛型的类型
//用第二种方法创建的实现类
*/
public class GenernicInterfaceImpl2<I> implements GenernicInterface<I>{
    @Override
    public void method(I i){
       System.out.println(i);
    }
} 

下面讲讲泛型的通配符

/*
   泛型的通配符:
      ? :代表任意的数据类型
   使用方式:
          不能在创建对象时使用
          只能作为方法的参数使用
*/
public class Demo05Genernic{
   public static void main(String[] args){
      ArrayList<Integer> list01 = new ArrayList<>();
      list01.add(1);
      list01.add(2);
      ArrayList<String> list02 = new ArrayList<>();
      list02.add("a");
      list02.add("b");
    
      printArray(list01);
      printArray(list02);

      //ArrayList<?> list03 = new ArrayList<?>();错误写法,通配符 ?不能在创建对象时使用

   }
   /*
      定义一个方法,能遍历所有类型的ArrayList集合
      这时候我们不知道ArrayList集合使用什么数据类型,可以使用泛型的通配符 ? 来接受数据
   */
   public static void printArray(ArrayList<?> list){
       //使用迭代器遍历集合
       Iterator<?> it = list.iterator();//这是调用方法获取实现类的对象,不算创建对象时,所以可以用通配符 ?。**此出不能用Iterator<Object> 使用则会报错,文章最会会讲解** 
       while(it.hasNext()){
          //it.next()方法,取出的元素是Object,可以接受任何的数据类型
          Object obj = it.next();
          System.out.println(obj);
       }
   }
}

/*
   泛型的上限限定:?extends E 代表使用的泛型只能是E类的子类/本身
   泛型的下限限定:?super E 代表使用的泛型只能是E类型的父类/本身
*/
public class Demo06Genernic{
  public static void main(String[] args){
     Collection<Integer> coll =new ArrayList<>();
     Collection<String> coll2 = new ArrayList<>();
     Collection<Number> coll3 = new ArrayList<>();//Number类是Integer类的父类
     Collection<Object> coll4 = new ArrayList<>();
     
     getElement1(coll);
     getElement1(coll2);//报错,String类不是Number类的子类
  }
  //泛型的上限:此时的泛型 ?,必须是Number类或者Number类型的子类
  public static void getElement1(Collection<? extends Number> coll){}
  //泛型的下限:此时的泛型 ?,必须是Number类型或者Number类型的父类
  public static void getElement2(Collection<? super Number> coll){}
}

TIPS
这里补充两个个知识点:
1.、等与<?>之间的区别
2.泛型与的区别

、等与<?>之间的区别:
在刚接触到java泛型的学习的时候,分开来看,大家都容易能够知道它们的概念和使用方法,但是横向比较时,就会有点懵逼,感觉貌似都是一个意思啊,为啥要多此一举,下面就和详细讲一下它们之间的区别。
T、E等这些大写的字母,都没有实际的意义和区别,只是为了让程序的可读性更好,但它们与<?>之间就存在差别了,举个栗子:
【 ”“是类型参数】 【 ”<?>“是无界通配符】
类型参数是声明泛型类或泛型方法 ;无界通配符<?>是使用泛型类或泛型方法
声明泛型类的类型参数或声明泛型方法,声明泛型类不能用无界通配符<?>,通配符是拿来使用定义好的泛型的。
通俗的说,”T“是定义类或方法时声明的东西;”?“是调用时传入的东西

泛型与的区别:
在Java1.5 之前,没有泛型的情况下,通过对类型Object的引用来实现参数的”任意化“,”任意化“带来的缺点是要做显示的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型的好处是数据类型,是由创建对象时传入什么样的类型参数,就只能接收什么样的数据类型参数,不需要强制转换

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值