java知识点之泛型


1.泛型概述

泛型:JDK1.5以后出现的新特性,用于解决安全问题,是一个类型安全机制

优点:

1.将运行时期可能抛出的异常ClassCastException(),转移到编译时期,方便程序员解决问题。让运行期间的问题减少,且程序更加安全

2.避免使用时对象强制类型转换的麻烦

2.泛型使用

泛型格式:通过<>来定义要操作的引用数据类型

在使用java提供的对象时, 什么时候写泛型?

1.通常在集合框架中很常见,只要见到有<>出现就要定义泛型。当使用集合时,将集合中需要存储的数据类型作为参数传递到<>即可。
2.<>用于接收泛型的具体类型,且只能是引用数据类型,因此基本数据类型需要传入其包装类(例:int–>Integer)。

示例代码:

        //创建List集合子类ArrayList对象
        ArrayList<String> al=new ArrayList<String>();

说明:以上代码的作用即创建一个用于装载String类型数据的ArrayList对象,<>中传入的是需要装载的数据类型。


3.泛型类

什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,可以定义泛型来完成扩展

格式:

public class GenericDemo<T>{}

示例代码:

public class GenericDemo<T>{
    //利用泛型定义成员变量的数据类型
    private T input;
    //包含泛型的构造方法
    public GenericDemo(T input){
        this.input=input;
    }
}

说明:以上代码就定义了一个简单的泛型类。利用泛型定义成员变量以及构造方法参数的类型,只需要在使用时指明具体数据类型即可。 这就使得这个类具有了较好的拓展性。


4.泛型方法

当方法的参数为泛型时,该方法就成为了一个泛型方法

格式:

public <Q> void setItem(Q q){}

说明:

1.泛型类定义的泛型,是在整个类中都有效的。如果这个泛型被类中方法使用,那么在泛型类的对象明确要操作的具体类型后,所有使用了这个泛型的方法的数据类型就也同时确定了。

2.为了让同一方法可以操作不同的数据类型,且类型不确定,则可以将泛型定义在方法上,且该泛型仅在这个方法中有效

3.可以同时在类和方法上定义泛型
例:public <Q> void show(T t){}
注:T是泛型类定义的泛型,Q是在方法中定义的泛型

示例代码:

    //泛型方法
    public <Q> void setItem(Q q){
        System.out.println("设置值:"+q);
    }

说明:定义泛型方法只需要将泛型符号写在方法的返回值类型前即可。

5.静态泛型方法

静态方法不可以访问类上定义的泛型。如果静态方法操作的数据类型不确定,就只能将泛型定义在该方法上。静态泛型方法的定义方式与泛型方法的使用方式相似。

格式:

public static <W> void show(W w){}


6.泛型接口

泛型除了可以用来定义类,还可以用来定义接口

格式:

interface GenericImp<T>{}

示例代码:

//部分代码

//泛型接口
interface GenericImp<T>{
    public void show(T t);
}

//使用泛型接口
class GenericClaByImp implements GenericImp<String>{

    //实现抽象方法
    public void show(String t) {        
        System.out.println(t);
    }

}

说明:当自定义类使用泛型接口时,指明接口泛型的具体数据类型即可。


7.泛型限定

1.使用通配符

使用泛型时,若不确定泛型的具体数据类型,则可以使用通配符<?>的方式

示例代码:

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

public class GenericLimitDemo {

    //自定义通用输出元素的方法
    public void printElement(ArrayList<?> al){
        Iterator<?> it=al.iterator();
        while(it.hasNext()){
            System.out.println("元素--"+it.next());
        }
    }

    public static void main(String[] args) {
        //新建存储数字的ArrayList对象
        ArrayList<Integer> intergerArray=new ArrayList<Integer>();
        intergerArray.add(5);//int自动装箱
        intergerArray.add(4);
        intergerArray.add(15);
        intergerArray.add(12);

        //新建存储字符串的ArrayList对象
        ArrayList<String> stringArray=new ArrayList<String>();
        stringArray.add("demoElement1");
        stringArray.add("demoElement2");
        stringArray.add("demoElement3");
        stringArray.add("demoElement4");
        stringArray.add("demoElement5");

        //实例化测试类对象
        GenericLimitDemo gld=new GenericLimitDemo();
        //调用同一方法对不同数据类型的集合进行元素输出
        gld.printElement(intergerArray);
        gld.printElement(stringArray);
    }

}

说明:从代码中可以看到,定义的通用输出方法printElement()事先并不确定参数的数据类型通配符,因此可以使用通配符<?>代替参数集合的参数部分。<?>也相当于直接不指定泛型,即输出方法可以改为’printELement(ArrayList al)’这是java为了向下兼容,但是这样做并不严谨。

也可以用定义泛型方法的方式代替这种通配符操作

示例代码:

    //部分代码
    //使用泛型方法自定义通用输出元素的方法
    public <T> void printElement(ArrayList<T> al){
        Iterator<T> it=al.iterator();
        while(it.hasNext()){
            System.out.println("元素--"+it.next());
        }
    }

1.泛型的限定

场景引入1:

    //说明:Dog extends Animal
    ArrayList<Animal> al=new ArrayList<Dog>();

说明:使用集合框架时,必须保证在实例化对象时等号两侧的泛型数据类型一致。即使两侧的具体泛型类型具有继承关系也不允许。

场景引入2:

    //说明:Dog extends Animal
    //定义取出元素的方法
    public static void print(ArrayList<Animal> al){
        //省略具体代码
    }

    public static void main(String[] args){
        ArrayList<Dog> al=new ArrayList<Dog>();
        //调用输出方法输出装有Dog的ArrayList对象
        print(al);
    }

说明:以上代码定义了一个输出Animal集合对象的方法,在调用时却传入Animal的子类对象Dog,这样的操作会使程序报错。

解决方法:

<? extends E>:可以接收E类型或者E类型的子类型。即限定泛型上限。

<? super E>:可以接受E类型或者E的父类型。即限定泛型下限。

说明: 限定泛型参数的上限或下限即为泛型的参数提供了一个接收类型的范围

示例代码:

    //说明:Dog extends Animal
    //定义取出元素的方法
    public static void print(ArrayList<? extends Animal> al){
        Iterator<? extends Animal> it=al.iterator();
        while(it.hasNext()){
            //省略具体操作代码
        }
    }

    public static void main(String[] args){
        ArrayList<Dog> al=new ArrayList<Dog>();
        //调用输出方法输出装有Dog的ArrayList对象
        print(al);
    }

说明:以上代码段利用定义泛型上限的方式,使得print方法可以接受Animal对象或者其子类对象作为参数。因此,这里便体现了泛型限定对方法的拓展。

### Java 全面知识点与基础教程 #### 什么是Java(Generics)是一种提供编译时类检查的机制,允许在类、接口和方法中定义、传递和操作各种类的对象,而无需明确指定具体类。这种设计可以增强代码的可读性、可维护性,并减少类转换错误[^1]。 --- #### 的关键概念 ##### 1. **类** 类是指可以在类声明时使用一个或多个类参数的类。这些类参数可以用作字段、方法返回值或方法参数的占位符。 - **基本格式**: ```java public class Box<T> { private T content; public void setContent(T content) { this.content = content; } public T getContent() { return content; } } ``` - **注意事项**: - 类的实际类是在创建对象时指定的。 - 编译器会在运行时执行类擦除,因此无法在类中直接使用 `new T()` 创建实例[^3]。 --- ##### 2. **接口** 类似于类,接口也可以接受类参数。 - **基本格式**: ```java public interface Generator<T> { T next(); } ``` - **注意事项**: - 实现接口的类可以选择固定某个类或者保留特性。 ```java // 固定类 public class StringGenerator implements Generator<String> { @Override public String next() { return "Hello"; } } // 保持 public class GenericGenerator<T> implements Generator<T> { @Override public T next() { return null; // 或者其他逻辑 } } ``` --- ##### 3. **方法** 方法是指在其签名中包含自己的类参数的方法。即使该方法属于非类,它仍然可以独立于类的类参数工作。 - **基本格式**: ```java public <T> void method(T param) {} ``` - **示例**: ```java public class Util { public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) { return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue()); } } class Pair<K, V> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } } ``` --- ##### 4. **通配符** 通配符用于表示未知类,增强了使用的灵活性。 - **协变 (`<? extends T>`)**:只允许访问子类的数据。 ```java List<? extends Number> list = new ArrayList<>(); Number num = list.get(0); // 可以获取数据 // list.add(new Integer(1)); 不支持添加任何元素 ``` - **逆变 (`<? super T>`)**:只允许访问父类的数据。 ```java List<? super Integer> list = new ArrayList<>(); list.add(new Integer(1)); // 支持添加Integer及其子类 Object obj = list.get(0); // 获取到的是Object类 ``` - **无界通配符 (`<?>`)**:表示任意类。 ```java List<?> list = new ArrayList<>(); Object obj = list.get(0); ``` --- ##### 5. **类擦除** 类擦除是 Java 中的一个重要概念,指的是在编译阶段会移除所有的信息,在字节码层面不再存在具体的类约束。 - **影响**: - 运行时无法判断的具体类。 - 静态上下文中不允许使用变量。 - **解决方式**: 使用反射或其他工具(如 TypeToken),可以通过间接手段恢复部分类信息。 --- ##### 6. **数组与** 由于数组具有运行时类检查的能力,而依赖于编译期类检查,这使得两者之间存在冲突。因此,不能创建带有的数组。 - 错误示例: ```java List<String>[] stringLists = new List<String>[1]; // 编译报错 ``` - 替代方案: ```java List<String>[] stringLists = (List<String>[]) new List<?>[1]; ``` --- #### 总结 Java 的核心在于提升代码的安全性和复用能力。通过了解其核心概念(如类、接口、方法、通配符和类擦除),开发者能够更高效地构建灵活且健壮的应用程序。 --- ### 示例代码 以下是综合运用知识点的简单例子: ```java public class Main { public static void main(String[] args) { Box<Integer> integerBox = new Box<>(); integerBox.setContent(10); System.out.println(integerBox.getContent()); Pair<String, Integer> pair = new Pair<>("Key", 123); System.out.println(pair.getKey() + ": " + pair.getValue()); List<Number> numbers = Arrays.asList(1, 2.5f, 3L); printNumbers(numbers); List<Object> objects = Arrays.asList("String", new Object(), 42); addObject(objects, "New Element"); } public static <T> void printNumbers(List<? extends Number> list) { for (Number number : list) { System.out.print(number.doubleValue() + " "); } System.out.println(); } public static <T> void addObject(List<? super T> list, T element) { list.add(element); } } class Box<T> { private T content; public void setContent(T content) { this.content = content; } public T getContent() { return content; } } class Pair<K, V> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } } ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值