8. 泛型程序设计

东方马达步进电机AZM66AK-HS100+AZD-KD调试经验记录 东方马达步进电机AZM66AK-HS100+AZD-KD调试经验记录(20170803YC) 本次总结主要实现的功能如下:用MEXE02软件在步进电机驱动器中设置好控制动作,每个控制动作有一个特定的编号。之后通过PC和驱动器之间的485通讯,发送相关指令,使步进电机按照一定编号的控制动作运转。 参考资料:东方马达步进电机AZM66AK-HS100+AZD-KD的产品手册,可在官网下载。 A.... 阅读详情

学习路线:
0. 为什么要有泛型

  1. 泛型类:class Box
  2. 泛型方法:public static T getMiddle(T… values)
  3. 类型变量的限定:<T extends Comparable>
  4. 泛型代码和虚拟机:类型擦除
  5. 泛型的限制:不能 new T()、不能创建泛型数组等
  6. 通配符:? extends 和 ? super
  7. 反射与泛型

0. 为什么要有泛型

没有泛型时:

ArrayList list = new ArrayList();
list.add("Alice");

String name = (String) list.get(0);

问题是:

  • 取元素时需要强制类型转换。
  • 如果放错类型,编译器不一定能发现。
  • 错误可能拖到运行期才爆发。

有了泛型:

ArrayList<String> list = new ArrayList<>();
list.add("Alice");

String name = list.get(0);

这时:

  • list 只能放 String
  • get 出来的结果自动就是 String
  • 编译器会帮你检查类型错误

如果你写:

list.add(123);

编译器直接报错。

1. 泛型类

最简单的泛型类

public class Box<T> {
    private T value;

    public void set(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }
}

这里的 T 是类型参数。创建对象时再决定它具体是什么类型:

Box<String> stringBox = new Box<>();
stringBox.set("hello");

String s = stringBox.get();

泛型类可以有多个类型参数

public 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;
    }
}

使用:

Pair<String, Integer> score = new Pair<>("Alice", 95);

String name = score.getKey();
Integer points = score.getValue();

2. 泛型方法

泛型方法是方法自己带类型参数:

public static <T> T getFirst(T[] array) {
    return array[0];
}

调用时:

String[] names = {"Alice", "Bob"};
String firstName = getFirst(names);

Integer[] numbers = {1, 2, 3};
Integer firstNumber = getFirst(numbers);

注意这里的 写在返回类型前面:

public static <T> T getFirst(T[] array)

第一个 是声明类型参数,后面的 T 才是返回类型。如果没有前面的 ,编译器不知道 T 是什么。

泛型方法不一定在泛型类里
这是一个容易误解的地方。
普通类里也可以有泛型方法:

public class ArrayUtil {
    public static <T> T getFirst(T[] array) {
        return array[0];
    }
}

ArrayUtil 本身不是泛型类,但 getFirst 是泛型方法。
反过来,泛型类里的普通方法不一定是泛型方法:

public class Box<T> {
    private T value;

    public T get() {
        return value;
    }
}

这里 get() 使用了类上的 T,但它自己没有声明新的类型参数,所以它不是“泛型方法”,而是“泛型类中的普通方法”。

3. 类型变量的限定

有时候,泛型不能太自由。
比如你要写一个方法,找出数组中的最小值:

public static <T> T min(T[] values) {
    // 怎么比较两个 T?
}

问题来了:不是所有对象都能比较大小。
所以要限制 T 必须实现 Comparable:

public static <T extends Comparable<T>> T min(T[] values) {
    T smallest = values[0];

    for (T value : values) {
        if (value.compareTo(smallest) < 0) {
            smallest = value;
        }
    }

    return smallest;
}

这里:

<T extends Comparable<T>>

意思是:T 必须是实现了 Comparable 的类型。

比如 String、Integer 都可以比较,所以能用:

String[] words = {"apple", "orange", "banana"};
String minWord = min(words);

为什么用 extends,不用 implements
哪怕后面跟的是接口,也写 extends:

<T extends Comparable<T>>

而不是:

<T implements Comparable<T>> // 错

在泛型限定里,extends 统一表示“是某个类型的子类型”:

多个限定
一个类型变量可以有多个限定:

<T extends Comparable<T> & Serializable>

意思是:T 必须既能比较,又能序列化
如果同时有类和接口,类必须放在第一个:

<T extends Employee & Comparable<T> & Serializable>

不能写成:

<T extends Comparable<T> & Employee> // 错

因为 Java 的泛型擦除机制需要用第一个限定作为主要擦除类型。这个先记结论就好,后面讲类型擦除时会更清楚

限定之后能做什么
没有限定时:

public static <T> void test(T value) {
    value.compareTo(...); // 不行
}

因为 T 只能当成 Object 用。
有了限定:

public static <T extends Comparable<T>> void test(T value, T other) {
    value.compareTo(other); // 可以
}

编译器知道 value 至少是一个 Comparable。
再比如:

public static <T extends Number> double sum(T[] values) {
    double result = 0;

    for (T value : values) {
        result += value.doubleValue();
    }

    return result;
}

因为 T extends Number,所以可以调用:

value.doubleValue()

限定不等于继承泛型类
这一点也容易混。

class Box<T extends Number> {
    private T value;
}

这表示:Box 里面的 T 只能是 Number 或 Number 的子类
所以可以:

Box<Integer> a;
Box<Double> b;
Box<Number> c;

不可以:

Box<String> d; // 编译错误

但它不表示 Box 是 Box 的子类。
也就是说:

Box<Integer> intBox = new Box<>();
Box<Number> numberBox = intBox; // 错

这是泛型里非常重要的一条:
即使 Integer 是 Number 的子类,Box 也不是 Box 的子类。

4. 类型擦除

类型擦除是什么
Java 的泛型主要存在于编译期。编译器会检查类型是否安全,但生成字节码时,会把很多泛型类型信息“擦掉”。
比如你写:

public class Pair<T> {
    private T first;
    private T second;

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }
}

如果 T 没有限定,编译后大致会变成:

public class Pair {
    private Object first;
    private Object second;

    public Object getFirst() {
        return first;
    }

    public void setFirst(Object first) {
        this.first = first;
    }
}

也就是说:T 被擦除成 Object
所以:

Pair<String>
Pair<Integer>
Pair<Employee>

在运行时本质上都是同一个类:Pair

这就是为什么下面代码输出通常是 true:

Pair<String> p1 = new Pair<>();
Pair<Integer> p2 = new Pair<>();

System.out.println(p1.getClass() == p2.getClass());

因为运行时只有一个 Pair.class。

4.1 有上界时擦除成上界

如果类型变量有限定:

public class Interval<T extends Comparable<T>> {
    private T lower;
    private T upper;

    public T getLower() {
        return lower;
    }
}

T 不会擦除成 Object,而是擦除成它的第一个限定:

public class Interval {
    private Comparable lower;
    private Comparable upper;

    public Comparable getLower() {
        return lower;
    }
}

如果是多个限定:

<T extends Employee & Comparable<T> & Serializable>

擦除成第一个限定:Employee

这也是为什么多个限定中,类要放在最前面。

4.2 编译器会插入强制类型转换

你写:

Pair<String> pair = new Pair<>();
String s = pair.getFirst();

擦除后 getFirst() 实际返回的是 Object。
那为什么能赋给 String?
因为编译器帮你插入了强制类型转换:

String s = (String) pair.getFirst();

所以泛型不是让 JVM 真正生成一个 Pair 类,而是:
编译期检查类型安全
擦除泛型信息
必要时插入类型转换

4.3 桥方法

类型擦除还有一个比较经典的副作用:桥方法。
看这个例子:

class Pair<T> {
    public void setSecond(T second) {
        // ...
    }
}

class DateInterval extends Pair<LocalDate> {
    @Override
    public void setSecond(LocalDate second) {
        // ...
    }
}

泛型擦除后,父类方法大致变成:

public void setSecond(Object second)

子类方法是:

public void setSecond(LocalDate second)

问题来了:这两个方法参数不同,按普通规则不算重写。
但从源码语义上,DateInterval 明明是在重写 Pair 的 setSecond。
为了保持多态,编译器会在子类里生成一个桥方法:

public void setSecond(Object second) {
    setSecond((LocalDate) second);
}

这样通过父类引用调用时,多态仍然成立:

Pair<LocalDate> pair = new DateInterval();
pair.setSecond(LocalDate.now());

桥方法是编译器自动生成的,平时你不用手写,但它解释了为什么类型擦除后泛型方法仍能保持多态。

4.4 不能在运行时判断具体泛型参数

因为类型擦除,所以不能这样:

if (pair instanceof Pair<String>) {
    // 错
}

只能这样:

if (pair instanceof Pair) {
    // 可以
}

同理,运行时也不能区分:

ArrayList<String>
ArrayList<Integer>

它们都是:ArrayList

所以这类代码不可靠,也不允许

list instanceof ArrayList<String>

不能 new T()
很多人初学泛型会想写:

public class Box<T> {
    public T create() {
        return new T(); // 错
    }
}

为什么不行?
因为运行时 T 已经被擦除了。JVM 不知道你想创建的是 String、Employee 还是别的类型。
如果需要创建对象,通常要传入构造器、Class 或工厂:

public class Box<T> {
    private Supplier<T> supplier;

    public Box(Supplier<T> supplier) {
        this.supplier = supplier;
    }

    public T create() {
        return supplier.get();
    }
}

使用:

Box<Employee> box = new Box<>(Employee::new);
Employee e = box.create();

一句话总结类型擦除
Java 泛型是编译期的类型安全机制;运行时大多数泛型类型参数会被擦除成 Object 或上界类型。

5. 泛型的限制与局限性

这些限制看起来零散,但根源大多都是一个:类型擦除。也就是运行时 JVM 通常不知道 T 到底是什么。

  1. 不能用基本类型作为类型参数
    不能这样写:
ArrayList<int> numbers = new ArrayList<>(); // 错

必须写包装类型:

ArrayList<Integer> numbers = new ArrayList<>();

因为泛型类型参数必须是引用类型,不能是基本类型。

  1. 不能用 instanceof 检查参数化类型
    不能这样:
if (list instanceof ArrayList<String>) {
    // 错
}

因为运行时 ArrayList 和 ArrayList 都被擦除成 ArrayList。
只能这样:

if (list instanceof ArrayList) {
    // 可以
}

或者更推荐:

if (list instanceof ArrayList<?>) {
    // 可以
}

ArrayList<?> 表示“某种元素类型的 ArrayList”,但不关心具体是什么。

  1. 不能创建参数化类型数组
    不能这样:
ArrayList<String>[] lists = new ArrayList<String>[10]; // 错

为什么?
数组在运行时会记住自己的元素类型,而泛型在运行时被擦除了。这两套机制放在一起容易破坏类型安全。

比如如果允许:

ArrayList<String>[] lists = new ArrayList<String>[10];
Object[] objects = lists;
objects[0] = new ArrayList<Integer>();
String s = lists[0].get(0);

这里就可能把 ArrayList 塞进 ArrayList[] 里,最后取 String 时出问题。
所以 Java 干脆禁止创建参数化类型数组。
可以声明,但不能创建

ArrayList<String>[] lists;              // 可以声明
lists = new ArrayList<String>[10];      // 不可以创建

实际开发中通常用集合替代数组:

ArrayList<ArrayList<String>> lists = new ArrayList<>();
  1. 不能实例化类型变量
    不能这样:
public class Box<T> {
    public T create() {
        return new T(); // 错
    }
}

因为运行时不知道 T 是什么。
常见替代方案是传入 Supplier:

public class Box<T> {
    private final Supplier<T> supplier;

    public Box(Supplier<T> supplier) {
        this.supplier = supplier;
    }

    public T create() {
        return supplier.get();
    }
}

使用:

Box<Employee> box = new Box<>(Employee::new);
Employee e = box.create();

或者传入 Class,用反射创建,不过现在更推荐工厂或 Supplier。

  1. 不能创建泛型数组
    不能这样:
public class Stack<T> {
    private T[] elements = new T[10]; // 错
}

因为 T 被擦除,运行时不知道数组元素类型。
常见做法:用 Object[] 保存,取出时转换:

public class Stack<T> {
    private Object[] elements = new Object[10];
    private int size = 0;

    public void push(T value) {
        elements[size++] = value;
    }

    @SuppressWarnings("unchecked")
    public T pop() {
        return (T) elements[--size];
    }
}
  1. 泛型类的静态上下文不能使用类型变量 T
    不能这样:
public class Box<T> {
    private static T value; // 错
}

也不能:

public class Box<T> {
    public static T getValue() { // 错
        return null;
    }
}

原因是:T 属于某个具体的 Box 实例类型,而 static 属于整个类。
运行时只有一个 Box.class,不是:

Box<String>.class
Box<Integer>.class

如果允许 static T value,那 Box 和 Box 到底共享一个什么类型的静态变量?说不清。
不过静态方法可以声明自己的类型参数:

public class Box<T> {
    public static <U> U identity(U value) {
        return value;
    }
}

这里的 U 是静态方法自己的类型参数,和类的 T 无关。

  1. 不能抛出或捕获泛型类的异常
    不能让泛型类继承 Throwable:
class Problem<T> extends Exception { // 错
}

也不能捕获类型变量:

public static <T extends Throwable> void test() {
    try {
        // ...
    } catch (T e) { // 错
    }
}

因为异常捕获是在运行时按异常类型匹配的,而泛型类型参数会被擦除。
不过方法可以声明抛出类型变量:

public static <T extends Throwable> void doWork(T exception) throws T {
    throw exception;
}

这个可以,常见于一些高级库代码。

  1. 擦除后不能造成方法冲突
    比如:
public void print(List<String> list) {}

public void print(List<Integer> list) {}

这两个方法不能同时存在。
因为类型擦除后都变成:

public void print(List list) {}

6. 泛型通? extends 与 ? super

也就是这些写法:

List<?>
List<? extends Number>
List<? super Integer>

先抓住一个大方向:通配符是为了让泛型类型之间的关系更灵活。

因为 Java 泛型默认是不变的。

6.1 为什么需要通配符

我们知道:

Integer 是 Number 的子类

但这不代表:

List<Integer> 是 List<Number> 的子类

所以这段代码是错的:

List<Integer> integers = new ArrayList<>();
List<Number> numbers = integers; // 错

为什么 Java 不允许?
如果允许,就会出问题:

List<Integer> integers = new ArrayList<>();
List<Number> numbers = integers;

numbers.add(3.14); // Double 也是 Number
Integer x = integers.get(0); // 这里就炸了

所以 Java 禁止 List 赋给 List。
但现实中,我们经常需要一个方法能接收各种 Number 子类列表:

List<Integer>
List<Double>
List<BigDecimal>

这时就要用:

List<? extends Number>

6.2 上界通配符:? extends

List<? extends Number>

意思是:某种未知类型的 List,但这个未知类型一定是 Number 或 Number 的子类。
所以它可以接收:

List<Integer> integers = new ArrayList<>();
List<Double> doubles = new ArrayList<>();

List<? extends Number> numbers1 = integers;
List<? extends Number> numbers2 = doubles;

适合“读取”数据:

public static double sum(List<? extends Number> list) {
    double result = 0;

    for (Number n : list) {
        result += n.doubleValue();
    }

    return result;
}

这个方法可以接收:

sum(List.of(1, 2, 3));
sum(List.of(1.5, 2.5, 3.5));

为什么可以读成 Number?
因为不管那个未知类型具体是 Integer、Double 还是 BigDecimal,它至少都是 Number。
但是,它不能安全地写入具体元素:

public static void addNumber(List<? extends Number> list) {
    list.add(1);    // 错
    list.add(1.5);  // 错
}

为什么?
因为 list 可能实际是:

List<Double>

如果你往里面加 Integer,就不安全。
也可能实际是:
如果你往里面加 Integer,就不安全。
也可能实际是:

List<BigDecimal>

如果你往里面加 Double,也不安全。
所以对于:

List<? extends Number>

编译器只知道:里面的元素“至少能当作 Number 读出来”,但不知道具体能放什么进去。
唯一可以放的是:

list.add(null); // 可以,但没啥实际意义

所以记住:? extends T 适合读,不适合写。

6.3 下界通配符:? super

List<? super Integer>

意思是:某种未知类型的 List,但这个未知类型一定是 Integer 的父类型。

它可以接收:

List<Integer> integers = new ArrayList<>();
List<Number> numbers = new ArrayList<>();
List<Object> objects = new ArrayList<>();

List<? super Integer> a = integers;
List<? super Integer> b = numbers;
List<? super Integer> c = objects;

这种类型适合“写入”数据:

public static void addIntegers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    list.add(3);
}

为什么可以加 Integer?
因为不管实际列表是:

List<Integer>
List<Number>
List<Object>

都能安全接收一个 Integer。
但读取时就弱了:

Object x = list.get(0); // 可以
Integer y = list.get(0); // 错

为什么不能直接读成 Integer?
因为实际列表可能是:

List<Object>

里面原本可能放着 “hello”、new Object() 等,并不保证取出来是 Integer。
所以记住:? super T 适合写,不适合精确读;读出来只能当作 Object。

6.5 无界通配符:?

List<?>

意思是:某种未知元素类型的 List。

它和 List 不一样。
List 表示:这是一个元素类型明确为 Object 的列表,可以放任何 Object

比如:

List<Object> objects = new ArrayList<>();
objects.add("hello");
objects.add(123);

而:

List<?> list

表示:这是某种类型的列表,但我不知道具体是什么类型

它可能是:

List<String>
List<Integer>
List<Employee>

所以不能随便加元素:

list.add("hello"); // 错
list.add(123);     // 错

但可以读:

Object obj = list.get(0);

因为不管里面是什么,至少都是 Object。
常见用途:

public static void printAll(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

这个方法可以接收任何类型的 List:

printAll(List.of("a", "b"));
printAll(List.of(1, 2, 3));

6.6 三者对比

List<? extends Number>

我不知道具体是什么列表,但元素一定是 Number 的某种子类。
可以读成 Number,基本不能写。

List<? super Integer>

我不知道具体是什么列表,但它一定能接收 Integer。
可以写入 Integer,读出来只能当作 Object。

List<?>

我不知道具体是什么列表。
可以读成 Object,基本不能写。

7. 反射与泛型

先给结论:
Java 泛型大多会被类型擦除,但类文件里仍然会保留一部分“泛型签名”信息,反射可以读取这些信息。
所以这里有两个层次:

  1. 运行时对象的真实类型:通常看不到具体泛型参数。

  2. 类、字段、方法声明上的泛型签名:可以通过反射读取。

  3. 运行时对象看不到泛型参数
    比如:

ArrayList<String> names = new ArrayList<>();
ArrayList<Integer> numbers = new ArrayList<>();

System.out.println(names.getClass());
System.out.println(numbers.getClass());
System.out.println(names.getClass() == numbers.getClass());

输出类似:

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

运行时没有两个类:

ArrayList<String>
ArrayList<Integer>

只有一个:

ArrayList

所以你不能靠对象本身判断它是 ArrayList 还是 ArrayList。
这就是类型擦除。

  1. 但声明上的泛型信息可以读取
    比如有一个类:
import java.util.List;

public class UserRepository {
    private List<String> names;

    public List<String> getNames() {
        return names;
    }
}

虽然运行时 new ArrayList() 的对象不知道自己是 String 列表,但字段声明:

private List<String> names;

这个“声明信息”会保留在 class 文件的 Signature 属性里。
可以用反射读取:

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

public class ReflectionGenericDemo {
    public static void main(String[] args) throws Exception {
        Field field = UserRepository.class.getDeclaredField("names");

        Type genericType = field.getGenericType();
        System.out.println(genericType);

        if (genericType instanceof ParameterizedType pType) {
            Type rawType = pType.getRawType();
            Type[] typeArgs = pType.getActualTypeArguments();

            System.out.println(rawType);
            System.out.println(typeArgs[0]);
        }
    }
}

输出类似:

java.util.List<java.lang.String>
interface java.util.List
class java.lang.String

这说明反射能看到字段声明里的 List。

  1. Class 和 Type 的区别
    反射泛型里最容易迷糊的是这两个:
Class
Type

Class<?> 表示普通的运行时类:

String.class
Integer.class
ArrayList.class

但泛型类型可能不只是一个普通类,比如:

List<String>
T
? extends Number
List<? super Integer>

这些都不能简单用 Class 表示。
所以 Java 反射提供了更宽泛的接口:

java.lang.reflect.Type

Type 有几个常见子类型:

Class:普通类型,比如 String、Integer、ArrayList
ParameterizedType:参数化类型,比如 List<String>
TypeVariable:类型变量,比如 T
WildcardType:通配符类型,比如 ? extends Number
GenericArrayType:泛型数组,比如 T[]

可以这么理解:
Class 是 Type 的一种。
Type 比 Class 更能描述复杂泛型。

  1. 读取方法返回值的泛型类型
    比如:
class UserService {
    public List<String> findNames() {
        return List.of("Alice", "Bob");
    }
}

反射读取:

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

Method method = UserService.class.getMethod("findNames");

Type returnType = method.getGenericReturnType();

if (returnType instanceof ParameterizedType pType) {
    System.out.println(pType.getRawType()); // interface java.util.List
    System.out.println(pType.getActualTypeArguments()[0]); // class java.lang.String
}

注意:

method.getReturnType()

只能得到擦除后的类型:

interface java.util.List

而:

method.getGenericReturnType()

可以得到:

java.util.List<java.lang.String>

所以对比一下:

getReturnType()           // 擦除后的 Class
getGenericReturnType()    // 带泛型信息的 Type

字段也是类似:

field.getType()           // 擦除后的 Class
field.getGenericType()    // 带泛型信息的 Type

参数也是类似:

method.getParameterTypes()
method.getGenericParameterTypes()
  1. 读取泛型父类
    这在框架里很常见。
    比如:
class BaseDao<T> {
}

class User {
}

class UserDao extends BaseDao<User> {
}

可以通过反射读取 UserDao 继承 BaseDao 时传入的类型参数:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

Type superType = UserDao.class.getGenericSuperclass();

if (superType instanceof ParameterizedType pType) {
    Type actualType = pType.getActualTypeArguments()[0];
    System.out.println(actualType); // class User
}

很多框架会用这种方式做类型推断,比如:

class UserRepository extends Repository<User, Long>

框架可以读取到:

User
Long

但前提是:类型参数要写在类的继承声明上。

  1. 什么时候读不到?
    这点很重要。
    如果只是创建对象:
List<String> names = new ArrayList<>();

你拿 names.getClass(),只能得到:

class java.util.ArrayList

读不到 String。
因为局部变量的泛型信息通常不能通过普通反射拿到,而且对象本身也不保存 String 这个参数。
能读到的通常是:

字段声明
方法返回类型声明
方法参数类型声明
父类/接口泛型声明

比如:

private List<String> names;
public List<String> getNames()
class UserDao extends BaseDao<User>

这些“声明上的泛型签名”能读到。

一句话总结
反射不能从普通泛型对象里恢复被擦除的类型参数,但可以读取类文件中保留下来的字段、方法、父类、接口等声明位置的泛型签名。

所以:

new ArrayList<String>().getClass()

看不到 String。
但:

Field field = SomeClass.class.getDeclaredField("names");
field.getGenericType()

如果字段声明是:

List<String> names;

就能看到 String。

Social-Auto-Upload实战:如何用Python+Playwright实现抖音/B站/小红书一键三连发? 本文详细介绍了如何使用Python和Playwright框架构建Social-Auto-Upload自动化上传工具,实现抖音、B站、小红书等平台的一键多发布。通过模拟真实用户操作和Cookie管理技术,该工具能有效解决内容创作者跨平台发布的重复劳动问题,提升工作效率。 阅读详情

相关推荐

Python 静态导入与动态导入

python中模块的静态导入 与 动态导入(运行时指定模块),以及具体对应示例。

慢也好,步子小也好,往前走就好~ 717

第8章 泛型程序设计

第8章 泛型程序设计 8.1 为什么要使用泛型程序设计 泛型程序设计 泛型程序设计 (Generic programming) 意味着编写的代码可以被很多不同类型的对象所重用。 8.1.1 类型参数的好处 public class ArrayList { // before generic classes private Object[] elementData; ... public Object get(int i) { . . , } public void add(Object o) {

silencefly的博客 471

ST-Link V2的隐藏技能:多通道串口调试的终极指南

本文深入探讨了ST-Link V2的多通道串口调试功能,突破传统烧录工具的限制。通过详细的硬件配置、Arduino IDE设置和实战案例,展示了如何利用SWD接口实现多通道数据监控,提升STM32F103C8T6等开发板的调试效率。文章还提供了PCB布局优化和性能调优技巧,助力开发者充分发挥ST-Link V2的潜能。

weixin_29285605的博客 357

8.泛型程序设计

文章目录*泛型程序设计**8.2定义简单泛型类**8.3泛型方法**8.4类型变量的限定**8.5泛型代码和虚拟机**8.5.1类型擦除**8.5.2转换泛型表达式**8.5.3转换泛型方法**8.6限制与局限性**8.6.1不能用基本类型实例化类型参数**8.6.2运行时类型查询只适用于原始类型**8.6.3不能创建参数化类型的数组**8.6.5不能实例化类型变量**8.6.6不能构造泛型数组**8.6.7泛型类的静态上下文中类型变量无效**8.6.8不能抛出或捕获泛型类的实例**8.6.9可以取消对检查

EdSheeran的博客 620

temu登录接口逆向分析(含Py纯算)

【代码】temu登录接口逆向分析(含Py纯算)

数据精灵喵 1948

告别仿真失败!用Sigrity 2022 SystemSI搞定DDR4信号质量仿真的保姆级流程

本文详细介绍了如何使用Sigrity 2022 SystemSI工具进行DDR4信号完整性仿真,提供了一套避免仿真失败、提高仿真精度和加速仿真流程的保姆级方法。从模型验证、设计数据预处理到拓扑构建和参数提取,再到高级调试技巧和仿真加速方案,帮助硬件工程师系统性地解决DDR4信号质量仿真中的常见问题。

weixin_42537499的博客 343

chap8-泛型程序设计

chap8-泛型程序设计 泛型 泛型基础 Java5中引入泛型; ArrayList<String>:<>表示类型参数 定义简单泛型类: public class Pair<T> { private T first; private T second; public Pair(){first = null; second = null;} ... } //多个泛型类型 public class Pair<T , U> {

hello 369

西电 程序设计基础课程设计 作业

当时做的程序设计课程的作业. 一共五题, 以报告的形式提交. 质量应该比较高(高傲). 完整内容在这里 高精度计算 问题描述 涉及知识点: 数组, 流程控制, 函数等 要求: 用整型数组表示10进制大整数(超过 2322^{32}232 的整数), 数组的每个元素存储大整数的一位数字, 实现大整数的加减法. 问题分析 结构设计 可将高精度整数写成一个类. 成员变量有: int a[MAXN], len; bool negative; 其中, 数组a用来存储大整数的一个数位, a[1]为个位上的数字, a

Wings 2513

【Core Java】第8章 泛型程序设计

泛型程序设计1 什么是泛型2 为什么要使用泛型3 泛型基础泛型类泛型方法泛型接口4 通配符泛型的上下边界PECS原则5 类型擦除6 Java泛型使用限制1.Java泛型不能使用基本类型2.Java泛型不允许进行直接实例化3.Java泛型不允许进行静态化4.Java泛型不允许直接进行类型转换(通配符可以)5.Java泛型不允许直接使用instanceof运算符进行运行时类型检查(通配符可以)6.Ja...

ChangMU 274

【系统架构设计师-2026上半年真题】综合知识-参考答案及详解(75题完全回忆版)

【系统架构设计师-2026上半年真题】综合知识-参考答案及详解(75题完全回忆版)

数据知道的博客 3106

UG NX二次开发(C#)-外部模式-批量删除某些图层中的体对象和曲线对象

在 UG NX中,有时需要批量删除某文件夹下多个.prt文件中固定一些图层中的体对象和曲线对象,这时如果采用手动逐个打开模型,并选择针对的图层删除,即耗时也容易遗漏,因此采用外部模式来处理比较方便。本文介绍批量删除某些图层中的体对象和曲线对象。

GimiGimmy 3035

8.1为什么要使用泛型程序设计

8.1.1类型参数的好处 在Java增加泛型之前,泛型程序设计是用继承实现的。ArrayList类只维护一个Object引用的数组。 好处: 使得程序具有更好的可读性和安全性。 ...

Dreamlate_Spider的博客 288

「UG/NX」Block UI 曲线收集器CurveCollector

曲线收集器块用于选择一条或多条曲线,包括通过选择意图连接的末端曲线链。曲线选择意图用于此块,但它不支持“交点处停止”、“跟随圆角”、“中间链”和“特征内链”等规则选项。

何曾参静谧的博客 1091

Core Java, Volume I 第8章 泛型程序设计

参考: Java核心技术 卷一 基础知识 第10版

yzpbright的博客 375

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

《Java 核心卷1》ch 8 泛型程序设计

@author:posper @version:v 1.0 @date:2021/7/9-2021/7/10 ch 8 泛型程序设计ch 8 泛型程序设计8.1 为什么使用泛型?使用 Object[] 的缺点(2个)泛型概述泛型的好处8.2 泛型类泛型类概述泛型类定义语法泛型类的**注意事项**:(3 条)从泛型类派生子类泛型接口8.3 泛型方法8.4 类型变量的限定8.5 泛型类型代码和虚拟机类型擦除类型擦除泛型表达式类型擦除泛型方法Java 泛型转换的注意事项(4点)8.6 泛型的限制与局限性1、不能用

weixin_56915683的博客 584

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

《Java核心技术》学习笔记——第8章 泛型程序设计

版权声明:本文为博主ExcelMann的原创文章,未经博主允许不得转载。 第8章 泛型程序设计 作者:ExcelMann,转载需注明。 第8章内容目录: 为什么要使用泛型程序设计 定义简单泛型类 泛型方法 类型变量的限定 泛型代码和虚拟机 限制与局限性 泛型类型的继承规则 通配符类型 反射和泛型 本章内容将介绍实现自己的泛型代码所需了解的全部知识,这些知识大多数情况下是用来帮助排除自己代码的问题。 一、为什么要使用泛型程序设计 泛型程序设计意味着编写的代码可以对多种不同类型的对象重用。 在Java中增加

a602389093的博客 413

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Java基础之《Java核心卷1》第8章 泛型程序设计

Java核心卷第8章,泛型和通配符

widsoor的博客 436

【学习笔记】Java基础知识点——第8章·泛型程序设计

引入泛型的概念,包括自定义泛型。简单介绍了泛型代码和虚拟机,例如类型擦除与其局限性。

qq_45957915的博客 427

Java核心技术 卷一 第8章 泛型程序设计

。

诗人密语 276

读《Java核心技术 卷I》有感之第8章 泛型程序设计

学无止境   泛型存在的意义就是相对于强制转换类型的代码具有更好的安全性与可读性。 8.1 为什么要使用泛型程序设计   就是为了让编写的代码尽可能的被很多不同类型的对象所重用。 8.1.1 类型参数的好处   类型参数用于泛指类型,这是程序具有更好的可读性与安全性的原因。 ArrayList<String> files = new ArrayList<>(); //类型参数在构造函数中可省略 ArrayList<String> files = new Array.

方寸间沧海桑田的博客 426

8、泛型程序设计与c++标准模板库1、泛型程序设计的概念和术语

有效地利用已有的成果,将经典的、优秀的算法标准化、模块化,从而提高软件的生产率,是软件产业化的需求,为了实现这一需求,不仅需要面向对象设计思想,而且需要泛型程序设计思想。 c++语言提供的标准模板库(standard template library,STL)便是对象对象程序设计和泛型程序设计(Generic Programming)思想相结合的一个良好典范。 1、泛型程序设计的概念和术语 ...

weixin_30433075的博客 258

JavaSE复习_8 泛型程序设计

  今晚看了core Java的泛型部分,万万没有想到,当时看培训班视频入门的一带而过的泛型,有这样多的细节,整理了一下书里面提到的一些自认为的重点,方便以后观阅.由于是复习,一些基础知识跳过.   △泛型类型的限定:即<T extends Comparable>类型,T为绑定类型的子类型,而一个类型的变量或者通配符可以有多个限定,例如<T extends Comparable...

weixin_30865427的博客 97

java核心技术(8)泛型程序设计

泛型程序设计(Generic programming) 意味着编写的代码可以被很多不同类型的对象所重用。 比杂乱的使用Object对象然后再进行强制类型转换具有更好的可读性和安全性。尤其对于集合。 public static &lt;T extends Comparable&gt; T min(T [] a) 表示T应该是实现了Comparable接口的类(此处extends表示绑定) 虚...

u012449548的博客 279

Java 核心技术 卷I 基础知识 第 8 章泛型程序设计

8.1 为什么要使用泛型程序设计 8.1.1 类型参数的好处 8.1.2 谁想成为泛型程序员 8.2 定义简单泛型类 package pair1; /** * @version 1.00 2004-05-10 * @author Cay Horstmann */ public class Pair<T> { private T first; privat...

dldldl1994的博客 451
上一篇: 5:IDEA中git的使用--git fetch 和 git pull 和解决冲突
下一篇: IDEA中mybits的使用
偏爱自由 !
博客等级 码龄6年 12粉丝 45原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值