简述Java泛型

本人小白一枚,欢迎大家一起讨论学习,如有错误,还望大家指教。

Java泛型

简述:JDK1.5版本以后出现的新特征,用于解决一个安全问题,是一个安全机制。
格式:通过<T>来定义要操作的引用数据类型,此处T的标识符随便写,常见的如T、E、K、V等形式的参数常用于表示泛型。
好处:

  1. 将运行时期出现的问题ClassException,转移到了编译时期。方便解决问题,让程序运行时问题减少,提高安全性。
  2. 避免了强制转换麻烦。

理解泛型只有在编译阶段有效

public static void main(String[] args) {
        List<String> stringList = new ArrayList<String>();
        List<Integer> integerList = new ArrayList<Integer>();
        
        Class stringListClass = stringList.getClass();
        System.out.println(stringListClass);
        Class integerListClass = integerList.getClass();
        System.out.println(integerListClass);
        System.out.println(stringListClass.equals(integerListClass));
    }

class java.util.ArrayList
class java.util.ArrayList
true

我们简单了解下Java泛型中类型擦除机制:Java的泛型是伪泛型,因为在编译期间,所有的泛型信息都会被擦除掉。正确理解泛型概念的首要前提是理解类型擦出。Java中的泛型基本都是在编译器这个层次来实现的,在生成的Java字节码中不包含泛型中的泛型信息的,会在编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的List和List等类型,在编译后都会变成List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法避免在运行时刻出现类型转换异常的情况。类型擦除也是Java的泛型实现方法与C++模版机制实现方式之间的重要区别。

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        List<Integer> list = new ArrayList<>();
        list.add(123);
        list.getClass().getMethod("add", Object.class).invoke(list, "张翼德");
        System.out.println(list);
    }

我们定义个集合并将泛型定义为Integer类型,如果直接调用add方法,我们只能存储整型数据。不过当我们用反射调用add方法的时候,我们就可以存储任意类型(Object)。这说明了泛型在编译之后被擦除了,只保留了原始类型(Object)。
在使用java提供对象时,什么时候写泛型呢?
通常在集合框架中很常见,只要见到<>就要定义泛型。
实例:按照集合中字符串的长度排序。

public class Demo1 {
    public static void main(String[] args) {
        TreeSet<String> arrayList = new TreeSet<String>(new MyCompare());
        arrayList.add("ab");
        arrayList.add("b");
        arrayList.add("abc");
        arrayList.add("edf");
        arrayList.add("swxf");

        for(Iterator iterator = arrayList.iterator();iterator.hasNext();) {
            System.out.println(iterator.next());
        }
    }
}
class MyCompare implements Comparator {
    @Override
    public int compare(Object o1, Object o2) {
        String str1 = (String)o1;
        String str2 = (String)o2;
        int num = str1.length() - str2.length();
        if (num == 0) {
            return str1.compareTo(str2);
        }
        return num;
    }
}

在这里插入图片描述
泛型的使用
泛型有三种使用方式,分别是泛型类泛型接口泛型方法
泛型类
什么时候定义泛型类?
当类中要操作的引用类型不确定的时候,早时期定义Object来完成扩展。现在定义泛型类来完成扩展。
格式:class 类名<泛型标识符>
在这里插入图片描述
定义的泛型Generic类,在我们创建该类时,不一样强制声明泛型类实参。如果声明了,此时就会起到限制作用,增强了安全性。如果不传入泛型类实参的话,我们在使用泛型的方法或成员变量定义的类型就可以是任意类型。
在这里插入图片描述
注意: 不能对确切的泛型类使用instanceof操作,如下面的写法编译会报错。

if (object instanceof Generid<String>)

泛型接口
泛型接口与泛型类定义及使用基本相同。泛型接口常被用于各种类的生产器中。

//定义一个泛型接口
public interface Generator<T> {
    public T next();
}

当实现泛型接口

/**
 * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
 * 即:class FruitGenerator<T> implements Generator<T>{
 * 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
 */
class FruitGenerator<T> implements Generator<T>{
    @Override
    public T next() {
        return null;
    }
}

当实现泛型接口的类,传入泛型实参时:

/**
 * 传入泛型实参时:
 * 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T>
 * 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。
 * 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
 * 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。
 */
public class FruitGenerator implements Generator<String> {

    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override
    public String next() {
        Random rand = new Random();
        return fruits[rand.nextInt(3)];
    }
}

泛型方法
泛型类定义的类型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。为了让不同的方法可以操作不同的类型,而且类型还不确定。那么可以将泛型定义在方法上。
注意:静态方法不可以访问类上定义的泛型。如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。

class Demo
{
	
public <T> void show(T t)
	{System.out.println("show:" + t);}

	public <Q> void print(Q q)	{
		System.out.println("print:" + q);
	}
	//静态方法类型的写法
    public static <W> void method(W t)   {
		System.out.println("metnod" + t);
	} 
}

泛型的高级应用
? 通配符,也可以理解为占位符。
泛型的限定:
? extends E:可以接收E类型或者E的子类型(上限)。
? super E:可以接收E类型或者E的父类型(下限)。
使用上限和?是有区别的,当使用?时,代表的是任意类型,而使用上下限代表着一个限定的范围。

public class Demo {
    public static void main(String[] args) {
        ArrayList<Person> personList = new ArrayList<>();
        personList.add(new Person("刘德华"));
        personList.add(new Person("刘丛印"));
        printColl(personList);

        ArrayList<Student> studentList = new ArrayList<>();
        studentList.add(new Student("周润发"));
        studentList.add(new Student("周星驰"));
        printColl(studentList);
    }

    public static void printColl(Collection<? extends Person> collection) {
        Iterator<? extends Person> iterator = collection.iterator();
        
        while(iterator.hasNext()) {
            System.out.println(iterator.next().getName());
        }
    }
}

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

class Student extends Person{
    Student(String name) {
        super(name);
    }
}

我们自定义Car类,之后传入到printColl()方法中,此时编译会失败。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值