Maison Java javaDidacticiel Java泛型详解

Java泛型详解

Dec 19, 2016 pm 02:58 PM
泛型

  1. 概述
    在引入范型之前,Java类型分为原始类型、复杂类型,其中复杂类型分为数组和类。引入范型后,一个复杂类型
    就可以在细分成更多的类型。
    例如原先的类型List,现在在细分成List, List等更多的类型。
    注意,现在List, List是两种不同的类型,
    他们之间没有继承关系,即使String继承了Object。下面的代码是非法的
        List ls = new ArrayList();
        List lo = ls;
    这样设计的原因在于,根据lo的声明,编译器允许你向lo中添加任意对象(例如Integer),但是此对象是
    List,破坏了数据类型的完整性。
    在引入范型之前,要在类中的方法支持多个数据类型,就需要对方法进行重载,在引入范型后,可以解决此问题
    (多态),更进一步可以定义多个参数以及返回值之间的关系。
    例如
    public void write(Integer i, Integer[] ia);
    public void write(Double  d, Double[] da);
    的范型版本为
    public void write(T t, T[] ta);

    2. 定义&使用
     类型参数的命名风格为:
     推荐你用简练的名字作为形式类型参数的名字(如果可能,单个字符)。最好避免小写字母,这使它和其他的普通
     的形式参数很容易被区分开来。
     使用T代表类型,无论何时都没有比这更具体的类型来区分它。这经常见于泛型方法。如果有多个类型参数,我们
     可能使用字母表中T的临近的字母,比如S。
     如果一个泛型函数在一个泛型类里面出现,最好避免在方法的类型参数和类的类型参数中使用同样的名字来避免混
     淆。对内部类也是同样。
     
     2.1 定义带类型参数的类
     在定义带类型参数的类时,在紧跟类命之后的<>内,指定一个或多个类型参数的名字,同时也可以对类型参数的取
     值范围进行限定,多个类型参数之间用,号分隔。
     定义完类型参数后,可以在定义位置之后的类的几乎任意地方(静态块,静态属性,静态方法除外)使用类型参数,
     就像使用普通的类型一样。
     注意,父类定义的类型参数不能被子类继承。
     public class TestClassDefine {
         ....  
     }
     
     2.2 定义待类型参数方法
     在定义带类型参数的方法时,在紧跟可见范围修饰(例如public)之后的<>内,指定一个或多个类型参数的名字,
     同时也可以对类型参数的取值范围进行限定,多个类型参数之间用,号分隔。
     定义完类型参数后,可以在定义位置之后的方法的任意地方使用类型参数,就像使用普通的类型一样。
     例如:
     public T testGenericMethodDefine(T t, S s){
         ...
     }
     注意:定义带类型参数的方法,骑主要目的是为了表达多个参数以及返回值之间的关系。例如本例子中T和S的继
     承关系, 返回值的类型和第一个类型参数的值相同。
     如果仅仅是想实现多态,请优先使用通配符解决。通配符的内容见下面章节。
     public void testGenericMethodDefine2(List s){
         ...
     }
     应改为
     public void testGenericMethodDefine2(List<?> s){
         ...
     }
     
    3. 类型参数赋值
     当对类或方法的类型参数进行赋值时,要求对所有的类型参数进行赋值。否则,将得到一个编译错误。
     
     3.1 对带类型参数的类进行类型参数赋值
     对带类型参数的类进行类型参数赋值有两种方式
     第一声明类变量或者实例化时。例如
     List list;
     list = new ArrayList;
     第二继承类或者实现接口时。例如
     public class MyList extends ArrayList implements List {...} 
     
     3.2 对带类型参数方法进行赋值
     当调用范型方法时,编译器自动对类型参数进行赋值,当不能成功赋值时报编译错误。例如
     public T testGenericMethodDefine3(T t, List list){
         ...
     }
     public T testGenericMethodDefine4(List list1, List list2){
         ...
     }
     
     Number n = null;
     Integer i = null;
     Object o = null;
     testGenericMethodDefine(n, i);//此时T为Number, S为Integer
     testGenericMethodDefine(o, i);//T为Object, S为Integer
     
     List list1 = null;
     testGenericMethodDefine3(i, list1)//此时T为Number
     
     List list2 = null;
     testGenericMethodDefine4(list1, list2)//编译报错
     
     3.3 通配符
     在上面两小节中,对是类型参数赋予具体的值,除此,还可以对类型参数赋予不确定值。例如
     List unknownList;
     List unknownNumberList;
     List unknownBaseLineIntgerList; 
     注意: 在Java集合框架中,对于参数值是未知类型的容器类,只能读取其中元素,不能像其中添加元素,
     因为,其类型是未知,所以编译器无法识别添加元素的类型和容器的类型是否兼容,唯一的例外是NULL

     List listString;
     List unknownList2 = listString;
     unknownList = unknownList2;
     listString = unknownList;//编译错误
     
    4. 数组范型
     可以使用带范型参数值的类声明数组,却不可有创建数组
     List[] iListArray;
     new ArrayList[10];//编译时错误
     
    5. 实现原理

    5.1. Java范型时编译时技术,在运行时不包含范型信息,仅仅Class的实例中包含了类型参数的定义信息。
    泛型是通过java编译器的称为擦除(erasure)的前端处理来实现的。你可以(基本上就是)把它认为是一个从源
    码到源码的转换,它把泛型版本转换成非泛型版本。
    基本上,擦除去掉了所有的泛型类型信息。所有在尖括号之间的类型信息都被扔掉了,因此,比如说一个
    List类型被转换为List。所有对类型变量的引用被替换成类型变量的上限(通常是Object)。而且,
    无论何时结果代码类型不正确,会插入一个到合适类型的转换。
           T badCast(T t, Object o) {
             return (T) o; // unchecked warning
           }
    类型参数在运行时并不存在。这意味着它们不会添加任何的时间或者空间上的负担,这很好。不幸的是,这也意味
    着你不能依靠他们进行类型转换。

    5.2.一个泛型类被其所有调用共享
    下面的代码打印的结果是什么?
           List l1 = new ArrayList();
           List l2 = new ArrayList();
           System.out.println(l1.getClass() == l2.getClass());
    或许你会说false,但是你想错了。它打印出true。因为一个泛型类的所有实例在运行时具有相同的运行时类(class),
    而不管他们的实际类型参数。
    事实上,泛型之所以叫泛型,就是因为它对所有其可能的类型参数,有同样的行为;同样的类可以被当作许多不同
    的类型。作为一个结果,类的静态变量和方法也在所有的实例间共享。这就是为什么在静态方法或静态初始化代码
    中或者在静态变量的声明和初始化时使用类型参数(类型参数是属于具体实例的)是不合法的原因。

    5.3. 转型和instanceof
    泛型类被所有其实例(instances)共享的另一个暗示是检查一个实例是不是一个特定类型的泛型类是没有意义的。
           Collection cs = new ArrayList();
           if (cs instanceof Collection) { ...} // 非法
    类似的,如下的类型转换
    Collection cstr = (Collection) cs;
    得到一个unchecked warning,因为运行时环境不会为你作这样的检查。

    6. Class的范型处理
    Java 5之后,Class变成范型化了。
    JDK1.5中一个变化是类 java.lang.Class是泛型化的。这是把泛型扩展到容器类之外的一个很有意思的例子。
    现在,Class有一个类型参数T, 你很可能会问,T 代表什么?它代表Class对象代表的类型。比如说,
    String.class类型代表 Class,Serializable.class代表 Class
    这可以被用来提高你的反射代码的类型安全。
    特别的,因为 Class的 newInstance() 方法现在返回一个T, 你可以在使用反射创建对象时得到更精确的类型。
    比如说,假定你要写一个工具方法来进行一个数据库查询,给定一个SQL语句,并返回一个数据库中符合查询条件
    的对象集合(collection)。
    一个方法是显式的传递一个工厂对象,像下面的代码:
    interface Factory {
          public T[] make();
    }
    public Collection select(Factory factory, String statement) { 
           Collection result = new ArrayList();
           /* run sql query using jdbc */
           for ( int i=0; i<10; i++ ) { /* iterate over jdbc results */
    T item = factory.make();
    /* use reflection and set all of item’s fields from sql results */
    result.add( item );
    }
    return result;
    }
    你可以这样调用:
    select(new Factory(){ 
        public EmpInfo make() { 
            return new EmpInfo();
            }
           } , ”selection string”);
    也可以声明一个类 EmpInfoFactory 来支持接口 Factory:
    class EmpInfoFactory implements Factory { ...
        public EmpInfo make() { return new EmpInfo();}
    }
    然后调用:
    select(getMyEmpInfoFactory(), "selection string");
    这个解决方案的缺点是它需要下面的二者之一:
    调用处那冗长的匿名工厂类,或为每个要使用的类型声明一个工厂类并传递其对象给调用的地方
    这很不自然。
    使用class类型参数值是非常自然的,它可以被反射使用。没有泛型的代码可能是:
    Collection emps = sqlUtility.select(EmpInfo.class, ”select * from emps”); ...
    public static Collection select(Class c, String sqlStatement) { 
        Collection result = new ArrayList();
        /* run sql query using jdbc */
        for ( /* iterate over jdbc results */ ) { 
            Object item = c.newInstance();
            /* use reflection and set all of item’s fields from sql results */
            result.add(item);
        }
            return result;
    }
    但是这不能给我们返回一个我们要的精确类型的集合。现在Class是泛型的,我们可以写:
    Collection emps=sqlUtility.select(EmpInfo.class, ”select * from emps”); ...
    public static Collection select(Classc, String sqlStatement) { 
        Collection result = new ArrayList();
        /* run sql query using jdbc */
        for ( /* iterate over jdbc results */ ) { 
            T item = c.newInstance();
            /* use reflection and set all of item’s fields from sql results */
            result.add(item);
        } 
        return result;
    }
    来通过一种类型安全的方式得到我们要的集合。
    这项技术是一个非常有用的技巧,它已成为一个在处理注释(annotations)的新API中被广泛使用的习惯用法。

    7. 新老代码兼容

    7.1. 为了保证代码的兼容性,下面的代码编译器(javac)允许,类型安全有你自己保证
    List l = new ArrayList();
    List l = new ArrayList();

    7.2. 在将你的类库升级为范型版本时,慎用协变式返回值。
    例如,将代码
    public class Foo { 
        public Foo create(){
            return new Foo();
        }
    }

    public class Bar extends Foo { 
        public Foo create(){
            return new Bar();
        } 
    }
    采用协变式返回值风格,将Bar修改为
    public class Bar extends Foo { 
        public Bar create(){
            return new Bar();
        } 
    }
    要小心你类库的客户端。


    更多Java泛型详解相关文章请关注PHP中文网!

    Déclaration de ce site Web
    Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

    Outils d'IA chauds

    Undresser.AI Undress

    Undresser.AI Undress

    Application basée sur l'IA pour créer des photos de nu réalistes

    AI Clothes Remover

    AI Clothes Remover

    Outil d'IA en ligne pour supprimer les vêtements des photos.

    Undress AI Tool

    Undress AI Tool

    Images de déshabillage gratuites

    Clothoff.io

    Clothoff.io

    Dissolvant de vêtements AI

    AI Hentai Generator

    AI Hentai Generator

    Générez AI Hentai gratuitement.

    Article chaud

    R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
    4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O. Meilleurs paramètres graphiques
    4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O. Comment réparer l'audio si vous n'entendez personne
    4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O. Commandes de chat et comment les utiliser
    4 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌

    Outils chauds

    Bloc-notes++7.3.1

    Bloc-notes++7.3.1

    Éditeur de code facile à utiliser et gratuit

    SublimeText3 version chinoise

    SublimeText3 version chinoise

    Version chinoise, très simple à utiliser

    Envoyer Studio 13.0.1

    Envoyer Studio 13.0.1

    Puissant environnement de développement intégré PHP

    Dreamweaver CS6

    Dreamweaver CS6

    Outils de développement Web visuel

    SublimeText3 version Mac

    SublimeText3 version Mac

    Logiciel d'édition de code au niveau de Dieu (SublimeText3)

    Les fonctions génériques résolvent-elles le problème des types de paramètres variadiques dans Golang ? Les fonctions génériques résolvent-elles le problème des types de paramètres variadiques dans Golang ? Apr 16, 2024 pm 06:12 PM

    Les fonctions génériques dans Go résolvent le problème des types variadiques : les fonctions génériques permettent de spécifier les paramètres de type au moment de l'exécution. Cela permet d'écrire des fonctions capables de gérer des arguments de différents types. Par exemple, la fonction Max est une fonction générique qui accepte deux paramètres comparables et renvoie la plus grande valeur. En utilisant des fonctions génériques, nous pouvons écrire du code plus flexible et général capable de gérer différents types de paramètres.

    Scénarios d'application spécifiques des génériques dans Golang Scénarios d'application spécifiques des génériques dans Golang May 04, 2024 am 11:45 AM

    Scénarios d'application des génériques dans Go : Opérations de collecte : Créez des opérations de collecte adaptées à tout type, comme le filtrage. Structures de données : écrivez des structures de données à usage général telles que des files d'attente, des piles et des cartes pour stocker et manipuler divers types de données. Algorithmes : écrivez des algorithmes à usage général tels que le tri, la recherche et la réduction qui peuvent gérer différents types de données.

    Quelles sont les limites supérieure et inférieure des fonctions génériques Java ? comment utiliser? Quelles sont les limites supérieure et inférieure des fonctions génériques Java ? comment utiliser? Apr 26, 2024 am 11:45 AM

    Les fonctions génériques Java permettent de définir des limites supérieures et inférieures. Extends spécifie que le type de données accepté ou renvoyé par une fonction doit être un sous-type du type spécifié, par ex. La limite inférieure (super) spécifie que le type de données accepté ou renvoyé par une fonction doit être un supertype du type spécifié, par ex. L'utilisation de génériques améliore la réutilisabilité et la sécurité du code.

    Explorez les avantages et les utilisations des génériques à Golang Explorez les avantages et les utilisations des génériques à Golang Apr 03, 2024 pm 02:03 PM

    Réponse : Les génériques Golang sont un outil puissant pour améliorer la réutilisabilité, la flexibilité, la sécurité des types et l'évolutivité du code. Description détaillée : Avantages : Réutilisabilité du code : algorithmes et structures de données communs Flexibilité : création d'instances de types spécifiques au moment de l'exécution Sécurité des types : vérification du type au moment de la compilation Extensibilité : facile à étendre et à personnaliser Objectif : fonctions communes : tri, comparaison Structures de données communes telles que les listes , cartes, piles, etc. Alias ​​de type : simplifiez les déclarations de type Génériques contraints : assurez la sécurité des types

    Application des génériques Java dans le développement Android Application des génériques Java dans le développement Android Apr 12, 2024 pm 01:54 PM

    L'application de génériques dans le développement Android améliore la réutilisabilité, la sécurité et la flexibilité du code. La syntaxe consiste à déclarer une variable de type T qui peut être utilisée pour manipuler des données paramétrées par type. Les génériques en action incluent des adaptateurs de données personnalisés, permettant à l'adaptateur de s'adapter à tout type d'objet de données personnalisé. Android fournit également des classes de listes génériques (telles que ArrayList) et des méthodes génériques permettant la manipulation de paramètres de différents types. Les avantages de l'utilisation de génériques incluent la réutilisabilité, la sécurité et la flexibilité du code, mais il faut veiller à spécifier les limites correctes et à les utiliser avec modération pour garantir la lisibilité du code.

    Quelles sont les limitations des fonctions génériques dans Golang ? Quelles sont les limitations des fonctions génériques dans Golang ? Apr 16, 2024 pm 05:12 PM

    Limitations des fonctions génériques Go : seuls les paramètres de type sont pris en charge, les paramètres de valeur ne sont pas pris en charge. La récursion des fonctions n'est pas prise en charge. Les paramètres de type ne peuvent pas être spécifiés explicitement, ils sont déduits par le compilateur.

    Quel est l'impact des génériques Golang sur les signatures et les paramètres des fonctions ? Quel est l'impact des génériques Golang sur les signatures et les paramètres des fonctions ? Apr 17, 2024 am 08:39 AM

    L'impact des génériques sur les signatures et les paramètres des fonctions Go comprend : Paramètres de type : les signatures de fonction peuvent contenir des paramètres de type, spécifiant les types que la fonction peut utiliser. Contraintes de type : les paramètres de type peuvent avoir des contraintes qui spécifient les conditions qu'ils doivent satisfaire. Inférence de type de paramètre : le compilateur peut déduire le type de paramètres de type non spécifiés. Spécification des types : les types de paramètres peuvent être explicitement spécifiés pour appeler des fonctions génériques. Cela augmente la réutilisabilité et la flexibilité du code, vous permettant d'écrire des fonctions et des types pouvant être utilisés avec plusieurs types.

    Comment les types d'énumération Java fonctionnent-ils avec les génériques ? Comment les types d'énumération Java fonctionnent-ils avec les génériques ? May 04, 2024 am 08:36 AM

    La combinaison des types d'énumération et des génériques en Java : lors de la déclaration d'une énumération avec des génériques, vous devez ajouter des crochets angulaires et T est le paramètre de type. Lors de la création d'une classe générique, vous devez également ajouter des crochets angulaires, T est un paramètre de type qui peut stocker n'importe quel type. Cette combinaison améliore la flexibilité du code, la sécurité du type et simplifie le code.

    See all articles