Java基础教程——泛型

泛型

Generics:泛型,愿意指“无商标的”。

泛型,可以理解为“宽泛的数据类型”,就是将类型由原来的具体的类型泛化。

泛型在建立对象时不指定类中属性的具体类型,而是在声明及实例化对象时由外部指定。泛型可以提高数据安全性。

List中应用了泛型,在编译期对数据类型进行严格 检查,如果类型不匹配,编译无法通过。
示例 :

public interface List<E> extends Collection<E>

E:Element

T:Type



泛型的本质是为了参数化类型,即在不创建新类型的情况下,通过泛型指定的不同类型(类型形参),调用时传入具体的类型(类型实参)。

在使用泛型过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

泛型类

public class 泛型_类 {
    public static void main(String[] args) {
        车<Girl> v = new 车<Girl>();
        v.add(new Girl());
        // 不让上车
        // v.add(new Boy());
    }
}
abstract class Person {
}
class Boy extends Person {
}
class Girl extends Person {
}
class 车<T> {
    public T add(T arg) {
        System.out.println(arg.getClass());
        return arg;
    }
}

泛型接口

泛型接口与泛型类的定义及使用基本相同。

public interface Generator<T> {
    public T getS();
}

泛型接口的两种使用方法(以ArrayList和Scanner为例):

import java.util.*;
interface IGenerics<E> {
    void m(E e);
}
// 仿照ArrayList<E>,implements List<E>
// 实现类还是泛型
class MyList<E> implements IGenerics<E> {
    @Override
    public void m(E e) {
        List<E> lst = new ArrayList<>();
    }
}
// 仿Scanner类, implements Iterator<String>
// 直接指定泛型为String
class MyScanner implements IGenerics<String> {
    @Override
    public void m(String e) {
        Scanner sc = new Scanner(System.in);
    }
}
public class 泛型接口 {
    public static void main(String[] args) {
        // 定义时未指定类型,实例化需要指定类型
        MyList<Integer> my1 = new MyList<>();
        my1.m(1);
        // 定义时已经指定类型,实例化不用指定
        MyScanner my2 = new MyScanner();
        my2.m("A");
    }
}

泛型方法

泛型定义在修饰符和返回类型之间,在参数列表中使用泛型。
泛型方法主要针对参数有泛型(如集合)的场景,如果把下例的参数List改为数组则和Object没有太大区别

import java.util.*;
public class 泛型方法 {
    public static <T> int find(List<T> lst, T a) {
        int indexOf = -1;
        if (lst != null) {
            indexOf = lst.indexOf(a);
        }
        return indexOf;
    }
    public static void main(String[] args) {
        int find = find(new ArrayList<String>(), "A");
        System.out.println(find);
        // 编译不通过:find(new ArrayList<Integer>(), "A");
    }
}

说明:

泛型的参数类型只能是类类型,不能是简单类型。比如,<int>是不可使用的。
可以声明多个泛型参数类型,比如<T,P,Q…>,同时还可以嵌套泛型,例如:<List<String>>。
泛型的参数类型可以使用extends语句,例如<T extends superclass>。
使用extends语句将限制泛型参数的适用范围,只能是指定类型的子类。
|-例如:<T extends Collection> ,则表示该泛型参数的使用范围是所有实现了Collection接口的calss。如果传入<String>则程序编译出错。

示例【T extends Collection】:

import java.util.*;
public class 泛型extends {
    public static <A extends Collection> void find(A arg1, int arg2) {
    }
    public static void main(String[] args) {
        find(new ArrayList(), 5);
        // 不是Collection的子类,编译不通过:
        // find(new String(), 5);
    }
}

<?>通配符

<?>用于承接不同类型的泛型对象。

不知道使用什么类型接收数据时,使用?通配符。

此时只能接收数据,不能往该集合中存储数据。

import java.util.*;
public class 泛型_通配符 {
    public static void main(String[] args) {
        List<String> lst1 = new ArrayList<String>();
        lst1.add("A");
        List<Integer> lst2 = new ArrayList<Integer>();
        lst2.add(1);
        printList_T(lst1);
        printList_T(lst2);
        System.out.println("----------");
        printList(lst1);
        printList(lst2);
    }
    // 用常规泛型写,比较麻烦
    private static <T> void printList_T(List<T> list) {
        list.add(list.get(0));
        for (int i = 0; i < list.size(); i++) {
            Object o = list.get(i);
            System.out.println(o);
        }
    }
    // 用泛型通配符写简单
    private static void printList(List<?> list) {
        // ?不能写入,此处编译错误:list.add(list.get(0));
        for (int i = 0; i < list.size(); i++) {
            Object o = list.get(i);
            System.out.println(o);
        }
    }
}

通配符界限

<? extends T>:是 “上界通配符(Upper Bounds Wildcards)”,泛型只能是T的子类/本身
<? super T>:是 “下界通配符(Lower Bounds Wildcards)”,泛型只能是T的父类/本身
参见:泛型通配符界限问题: https://www.cnblogs.com/tigerlion/p/10659515.html

定义三个类,Fruit->Apple->RedApple,声明ArrayList,有如下现象:

import java.util.*;
public class 泛型_通配符边界2 {
    public static void main(String[] args) {
        ArrayList<Apple> p1 = new ArrayList<Apple>();
        // 【震惊!装得了苹果,装不了红苹果】
        // ↓Type mismatch:
        // cannot convert from ArrayList<RedApple> to ArrayList<Apple>
        // ArrayList<Apple> p2 = new ArrayList<RedApple>();
    }
}
class Fruit {
}
class Apple extends Fruit {
}
class RedApple extends Apple {
}

解决方案就是

【1.<? extends Fruit>,定类型继承关系的上界】
【2.<? super Apple>,确定类型继承关系的下界】

解决方案如下,照着注释看便是

package ah;
import java.util.*;
// 先定义三个类:水果、苹果、红苹果
class Fruit {
}
class Apple extends Fruit {
}
class RedApple extends Apple {
}
public class 泛型_通配符边界 {
    public static void main(String[] args) {
        ArrayList<Apple> p1 = new ArrayList<Apple>();
        // 【震惊!装得了苹果,装不了红苹果】
        // ↓Type mismatch:
        // cannot convert from ArrayList<RedApple> to ArrayList<Apple>
        // ArrayList<Apple> p2 = new ArrayList<RedApple>();
        // 【1.<? extends Fruit>,定类型继承关系的上界:】
        // 能装Apple以及一切Apple的派生类
        ArrayList<? extends Apple> p3 = new ArrayList<Apple>();
        p3 = new ArrayList<RedApple>();
        // ↓上层的类不接受
        // Type mismatch:
        // cannot convert from ArrayList<Fruit> to ArrayList<? extends Apple>
        // p3 = new ArrayList<Fruit>();
        // 【然而,extends是只读的,不能写入】
        // p3.add(new Apple());
        if (p3.size() != 0) {
            Apple apple = p3.get(0);
        }
        // 【2.<? super Apple>,确定类型继承关系的下界】
        // 能装苹果以及一切苹果的基类
        ArrayList<? super Apple> p4 = new ArrayList<Apple>();
        p4 = new ArrayList<Fruit>();
        p4 = new ArrayList<Apple>();
        // Type mismatch:
        // cannot convert from ArrayList<RedApple> to ArrayList<? super Apple>
        // p4 = new ArrayList<RedApple>();
        // 【可读可写,读出来的是Object类型】
        p4.add(new RedApple());// 子类对象但是可以写进入,因为默认向上转型
        Object object = p4.get(0);
        System.out.println(object);
        // 最后,其实JDK 7之后,后面的<>里什么都不用写
        List<Apple> p2 = new ArrayList<>();
    }
}

原文地址:https://www.cnblogs.com/tigerlion/p/11179214.html

时间: 2024-11-03 00:25:26

Java基础教程——泛型的相关文章

Java基础教程 - 组合

1. 什么是组合? 如果一个类的对象和另一个类满足"has-a"关系的话,我们就可以在一个类中,把另一个类的对象作为其对象成员. 什么是"has-a"关系,举个例子:现在有一个类LapTop.class,还有一个是Moniter.class.好显然,Laptop "has-a" Moniter,也就是说,他们是满足"has-a"关系的.这时候,我们就可以把Moniter作为Laptop的一个数据成员. class Laptop

java基础之泛型用法

1.什么是泛型 泛型是java中保障类型安全的一种机制.它通过对变量类型的假设,实现变量类型的进行限制,从而在一定程度上保证程序编译时变量类型的安全.反之,如果没有泛型,这些假设则只存在于程序员的大脑中,这样会导致类型转换的异常在程序运行时才被发现. //不用泛型 List list1 = new ArrayList(); list1.add(1); Object o = list1.get(0); //返回Object类型对象 Integer i = (Integer) o; //需预先知道类

Java基础:泛型

Java的泛型是什么呢, 就是类型的参数化,这得类型包括方法参数和返回值,也就是原本该是确定类型的地方换成了变量,把类型的确定时间向后延迟了. 在之前,学过"重载"的概念,重载是什么呢,举例来说 public class Test{ // 处理整型数据 void receive(int i) { System.out.println("Received one int data"); System.out.println("i="+i); } /

Java基础巩固----泛型

注:参考书籍:Java语言程序设计.本篇文章为读书笔记,供大家参考学习使用   1.使用泛型的主要优点是能够在编译时而不是在运行时检查出错误,提高了代码的安全性和可读性,同时也提高了代码的复用性. 1.1如何理解泛型的使用提高了代码的安全性? 答:例如这样一段代码: ArrayList<Date> arrayList = new ArrayList<Date>(); arrayList.add(new Date()); arrayList.add("data")

java基础之泛型的继承

关于泛型的基本介绍和理解请参考以下几篇文章,或查询更多资料: 泛?型?与?泛?型?继?承 泛型(包括泛型抽象类的继承) 泛型的继承测试 本篇以简单的List<>方式来进行说明. ArrayList<Object>继承了List<Object>, ArrayList<String>没有继承List<Object> List<?>等价于List<? extends Object> 请参考以下代码: /** * author D

Java基础教程:面向对象编程

Java基础教程:面向对象编程 Java语言概述 Java语言特点 1.Java为纯面向对象的语言,它能够直接反映现实生活中的对象.总之,Everything is object! 2.平台无关性.Java为解释型语言,编译器会把Java代码变成"""中间代码",然后在JVM上解释执行. 3.Java提供了很多内置的类库,这些类库简化了开发人员的程序设计工作,同时缩短了项目开发时间. 4.Java语言提供了对Web应用的支持. 5.Java语言提供了较好的安全性和健

Java基础教程:面向对象编程[2]

Java基础教程:面向对象编程[2] 三大特性 封装 封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部份包装.隐藏起来的方法.封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问. 使用封装我们可以对成员变量进行更精确的控制,同时隐藏信息,实现细节等. 方法: public class Person{ private String name; private int age; ? public int getAge(){ return age;

Java基础教程:JDBC编程

Java基础教程:JDBC编程 快速开始 什么是JDBC JDBC 指 Java 数据库连接,是一种标准Java应用编程接口( JAVA API),用来连接 Java 编程语言和广泛的数据库. JDBC API 库包含下面提到的每个任务,都是与数据库相关的常用用法. 制作到数据库的连接. 创建 SQL 或 MySQL 语句. 执行 SQL 或 MySQL 查询数据库. 查看和修改所产生的记录. 从根本上来说,JDBC 是一种规范,它提供了一套完整的接口,允许便携式访问到底层数据库,因此可以用 J

Java基础教程:HashTable与HashMap比较

Java基础教程:HashTable与HashMap比较 1.  关于HashMap的一些说法: a)  HashMap实际上是一个"链表散列"的数据结构,即数组和链表的结合体.HashMap的底层结构是一个数组,数组中的每一项是一条链表. b)  HashMap的实例有俩个参数影响其性能: "初始容量" 和 装填因子. c)  HashMap实现不同步,线程不安全.  HashTable线程安全 d)  HashMap中的key-value都是存储在Entry中的