百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

《Effective Java 3rd》3分钟速成(26-30) 泛型使用规则

toyiye 2024-05-25 20:11 16 浏览 0 评论

前言:

Chapter 5. Generics

  • 26. 不要使用原始类型
  • 27. 消除非检查警告
  • 28. 列表优于数组(泛型场景时)
  • 29. 优先考虑泛型
  • 30. 优先使用泛型方法

26. 不要使用原始类型

泛型相关概念

相关概念:

  • 泛型类或泛型接口:一个类或接口,它的声明有一个或多个类型参数(type parameters ),被称之为泛型类或泛型接口[JLS,8.1.2,9.1.2]。
  • 泛型类型(generic types):泛型类和接口统称为泛型类型(generic types);
  • 原始类型(raw type):每个泛型定义了一个原始类型(raw type),它是没有任何类型参数的泛型类型的名称[JLS,4.8]。 例如,对应于 List<E> 的原始类型是 List。
  • 原始类型的行为:就像所有的泛型类型信息都从类型声明中被清除一样。 它们的存在主要是为了与没有泛型之前的代码相兼容。

不使用原始类型的原因:

  • 使用原始类型(没有类型参数的泛型)是合法的(Java为了兼容),但是你不应该这样做。
  • 如果你使用原始类型,则会丧失泛型的所有安全性和表达上的优势。

原始类型的正确替代方案:

  • 安全替代方式是使用无限制通配符类型(unbounded wildcard types)。 如果要使用泛型类型,但不知道或关心实际类型参数是什么,则可以使用问号来代替。
  • 无限制通配符 Set<?> 与原始类型 Set 之间有什么区别? --通配符类型是安全的,原始类型不是(无限制通配符 Set<?> ≠ 原始类型 Set )。

另外,原始类型的可用场景:

  • 你必须在类字面值(class literals)中使用原始类型。 规范中不允许使用参数化类型(尽管它允许数组类型和基本类型)[JLS,15.8.2]。 换句话说,List.class,String[].class 和 int.class 都是合法的,但 List<String>.class 和 List<?>.class 都是不合法的。
  • 因为泛型类型信息在运行时被擦除,所以在无限制通配符类型以外的参数化类型上使用 instanceof 运算符是非法的。

总结:

  • 使用原始类型可能导致运行时异常,所以不要使用它们。
  • 原始类型只是为了与引入泛型机制之前的遗留代码进行兼容和互用而提供的。


27. 消除非检查警告

使用泛型编程时,会看到许多编译器警告:

  • 未经检查的强制转换警告;
  • 未经检查的方法调用警告;
  • 未经检查的参数化可变长度类型警告;
  • 以及未经检查的转换警告。

泛型编译警告处理方法:

  • 1、尽可能地消除每一个未经检查的警告。
  • 2、如果你不能消除警告,但你可以证明引发警告的代码是类型安全的,那么(并且只能这样)用 @SuppressWarnings("unchecked") 注解来抑制警告。
  • 3、每当使用 @SuppressWarnings(“unchecked”) 注解时,请添加注释,说明为什么是安全的。 这将有助于他人理解代码,更重要的是,这将减少有人修改代码的可能性,从而使计算不安全。

总结:

  • 1、未经检查的警告是重要的。 不要忽视他们。
  • 2、每个未经检查的警告代表在运行时出现 ClassCastException 异常的可能性。
  • 3、尽你所能消除这些警告。
  • 4、如果无法消除未经检查的警告,并证明是安全类型的,则在尽可能小的范围内使用 @SuppressWarnings(“unchecked”) 注解来禁止警告。并说明你决定在注释中抑制此警告的理由。


28. 列表优于数组(泛型场景)

数组和泛型不能很好地在一起混合使用。因为数组在两个重要方面与泛型不同:

  • 1、数组是协变的(covariant),泛型是不变的(invariant)。Item类型错误时,List编译时即会发现错误,Array到运行时才能发现错误。
  • 2、数组被具体化了(reified)。 这意味着数组在运行时知道并强制执行它们的元素类型。 如前所述,如果尝试将一个 String 放入 Long 数组中,得到一个 ArrayStoreException 异常。 相反,泛型通过擦除(erasure)来实现。 这意味着它们只在编译时执行类型约束,并在运行时丢弃(或擦除)它们的元素类型信息。


泛型数组的错误语法举例:

// Why generic array creation is illegal - won't compile!
List<String>[] stringLists = new List<String>[1];  // (1)
List<Integer> intList = List.of(42);               // (2)
Object[] objects = stringLists;                    // (3)
objects[0] = intList;                              // (4)
String s = stringLists[0].get(0);                  // (5)

总结:

  • 1、数组和泛型具有非常不同的类型规则。 数组是协变和具体化的; 泛型是不变的,类型擦除的。
  • 2、因此,数组提供运行时类型的安全性,但不提供编译时类型的安全性,对于泛型则是相反。
  • 3、一般来说,数组和泛型不能很好地混合工作。
  • 4、如果你发现把它们混合在一起,得到编译时错误或者警告,你的第一个冲动应该是用列表来替换数组。

29. 优先考虑泛型

参数化声明并使用 JDK 提供的泛型类型和方法通常不会太困难。 但编写自己的泛型类型有点困难,但值得努力学习。

堆栈容器(具体的数据类型改为泛型)的代码示例:

// Object-based collection - a prime candidate for generics
public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    // The elements array will contain only E instances from push(E).
    // This is sufficient to ensure type safety, but the runtime
    // type of the array won't be E[]; it will always be Object[]!
    @SuppressWarnings("unchecked")
    public Stack() {
        elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
    }
    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }

    // Appropriate suppression of unchecked warning
    public E pop() {
        if (size == 0) throw new EmptyStackException();
        // push requires elements to be of type E, so cast is correct
        @SuppressWarnings("unchecked") E result = (E) elements[--size];
        elements[size] = null; // Eliminate obsolete reference
        return result;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    private void ensureCapacity() {
        if (elements.length == size)
            elements = Arrays.copyOf(elements, 2 * size + 1);
    }
}

总结:

  • 1、泛型类型比需要在客户端代码中强制转换的类型更安全,更易于使用。
  • 2、当你设计新的类型时,确保它们可以在没有这种强制转换的情况下使用。 这通常意味着使类型泛型化。
  • 3、如果你有任何现有的类型,应该是泛型的但实际上却不是,那么把它们泛型化。
  • 4、这使这些类型的新用户的使用更容易,而不会破坏现有的客户端(条目 26)。


30. 优先使用泛型方法

正如类可以是泛型的,方法也可以是泛型的。 对参数化类型进行操作的静态工具方法通常都是泛型的。 集合中的所有“算法”方法(如 binarySearch 和 sort)都是泛型的。


泛型方法示例:

// Generic method
public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
    Set<E> result = new HashSet<>(s1);
    result.addAll(s2);
    return result;
}

泛型单例工厂:

因为泛型是通过擦除来实现的(详见第 28 条),所以可以使用单个对象进行所有必需的类型参数化,但是需要编写一个静态工厂方法来重复地为每个请求的类型参数化分配对象。 这种被称为泛型单例工厂(generic singleton factory)的模式,用于函数对象(function objects)(详见第 42 条),比如 Collections.reverseOrder 方法,偶尔也用于 Collections.emptySet 之类的集合。


自限定的类型:

<E extends Comparable <E >> 可以理解为「任何可以与自己比较的类型 E」,这或多或少精确地对应于相互可比性的概念。

// Returns max value in a collection - uses recursive type bound
public static <E extends Comparable<E>> E max(Collection<E> c) {
  if (c.isEmpty())
    throw new IllegalArgumentException("Empty collection");
  E result = null;
  for (E e : c)
    if (result == null || e.compareTo(result) > 0)
      result = Objects.requireNonNull(e);
  return result;
}

总结:

  • 1、像泛型类型一样,泛型方法比需要客户端对输入参数和返回值进行显式强制转换的方法更安全,更易于使用。
  • 2、像类型一样,你应该确保你的方法可以不用强制转换,这通常意味着它们是泛型的。
  • 3、应该泛型化现有的方法,其使用需要强制转换。 这使得新用户的使用更容易,而不会破坏现有的客户端(详见第 26 条)。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码