精华内容
下载资源
问答
  • Java 泛型方法

    万次阅读 2019-06-09 20:18:33
    定义泛型方法 泛型方法和类型通配符的区别 Java 7 的“菱形”语法与泛型构造器 设定通配符·下限 泛型方法与方法重载 Java 8 改进的类型推断 1. 定义泛型方法 假设需要实现这样一个方法:该方法负责将一个 Object ...

    本文包含:

    1. 定义泛型方法
    2. 泛型方法和类型通配符的区别
    3. Java 7 的“菱形”语法与泛型构造器
    4. 设定通配符·下限
    5. 泛型方法与方法重载
    6. Java 8 改进的类型推断

    1. 定义泛型方法

    假设需要实现这样一个方法:该方法负责将一个 Object 数组的所有元素添加到一个 Collection 集合中。考虑采用如下代码来实现该方法:
    在这里插入图片描述
    上面定义的方法没有任何问题,关键在于方法中的c 参数,它的数据类型是 Collection。正如前面介绍的,Collection 不是 Collection 的子类型—所以这个方法的功能很有限,它只能将 Object[] 数组的元素复制到元素为 Object (Object 的子类不行)的Collection 集合中,即下面代码会引起问题。
    在这里插入图片描述
    可见上面方法的参数类型不可以使用 Collection,
    使用通配符 Collection<?> 也不行,因为Java 不允许把对象放进一个未知类型的集合里。
    为解决这个问题,可以使用 Java 5 提供的泛型方法,在声明方法时定义一个或多个类型形参。泛型用法格式如下:
    在这里插入图片描述
    该泛型方法的方法签名比普通方法的方法签名多了类型形参声明,类型形参声明以尖括号括起来,多个类型形参直接以逗号(,)隔开,所有的类型形参声明放在方法修饰符和返回值类型之间。
    采用支持泛型的方法,就可以将上面的 fromArrayToCollection 方法改写为如下形式:
    在这里插入图片描述
    下面程序示范了完整用法:
    在这里插入图片描述
    在这里插入图片描述
    上面程序调用了一个泛型方法,该泛型方法中定义了一个 T 类型形参,这个 T 类型形参就可以在该方法内当成普通类型使用。与接口、类声明中定义的类型参数不同的是,方法声明中定义的形参只能在该方法内使用,而接口、类声明中定义的类型形参则可以在整个接口、类中使用。

    与类,接口中泛型参数不同的是,方法中的泛型参数无须显式传入实际类型参数,如上面程序所示,当程序调用 fromArrayToCollection() 方法时,无须在调用该方法前传入String、Object 等类型,但系统依然可以知道类型参数的数据类型,因为编译器根据实参推断类型实参的值,它通常推断出最直接的类型参数。例如,下面调用程序:
    在这里插入图片描述
    上面代码中 cs 是一个 Collection 类型,与方法定义时的 fromArrayToCollection(T[] a, Collection c)进行比较—只比较泛型参数,不难发现该 T 类型形参代表的实际类型是 String 类型。
    对于如下调用代码:
    在这里插入图片描述
    上面的 cn 是Collection 类型,与此方法的方法签名进行比较—只比较泛型参数,不难发现该T 类型形参代表了 Number 类型。
    在下面程序中:
    在这里插入图片描述在这里插入图片描述
    上面程序中定义了 test() 方法,该方法用于将前一个集合中的元素复制到下一个集合中,该方法中的两个形参from、to 的类型都是 Collection,这要求调用该方法时的两个集合实参中的泛型类型相同,否则编译器无法准确地推断出泛型方法中类型形参的类型。

    上面程序中定义了 test() 方法,该方法用于将集合中的元素复制到下一个集合中,该方法中的两个形参from、to 的类型都是 Collection,这要求调用该方法时的两个集合实参中的泛型类型相同,否则编译器无法准确地推断出泛型方法中类型形参的类型。

    上面程序中调用 test 方法传入两个实际参数,其中 as 的数据类型是 List,而 ao 的数据类型是 List,与泛型方法签名进行对比:test(Collection a,Collection c>,编译器无法正确识别 T 所代表的实际类型。
    为了避免此错误,可将方法改为如下:
    在这里插入图片描述在这里插入图片描述

    2. 泛型方法和类型通配符的区别

    大多数时候都可以使用泛型方法来代替类型通配符。例如,对于 Java 的Collection 接口中两个方法定义:
    在这里插入图片描述
    上面集合中两个方法的形参都采用了类型通配符的形式,也可以采用泛型方法的形式,如下所示:
    在这里插入图片描述
    上面方法使用了 泛型形式,这时定义类型形参时设定上限(其中 E 是Collection 接口里定义的类型形参,在该接口里 E 可以当成普通类型使用)
    上面两个方法中类型形参 T 只使用了一次,类型形参 T 产生的唯一效果是可以在不同的调用点传入不同的实际类型。对于这种情况,应该使用通配符。通配符就是被设计用来支持灵活的子类化的。
    泛型方法允许类型形参被用来表示方法的一个或多个参数之间的依赖关系,或者方法返回值与参数之间的类型依赖关系。如果没有这样的类型依赖关系,就不应该使用类型方法。
    在这里插入图片描述
    如果有需要,也可以同时使用泛型方法和通配符,如 Java 的 Collections.copy()方法。
    在这里插入图片描述
    在这里插入图片描述

    3. Java 7 的“菱形”语法与泛型构造器

    泛型构造器可以在构造器签名中声明类型形参,那么在调用构造器时就可以根据数据类型来推断类型形参的类型,程序员也可以显式的为构造器中的类型形参指定实际的类型。

    在这里插入图片描述

    4. 设定通配符下限

    假设自己实现一个工具方法:实现将 src 集合里的元素复制到 dest 集合里的功能,因为 dest 集合可以保存 src 集合里的所有元素,所以 dest 集合元素里的类型应该是 src 集合元素类型的父类。为了表示两个参数之间的类型依赖,可以考虑同时使用通配符、泛型参数来实现这些方法。
    在这里插入图片描述
    上面方法实现了前面的类型。现在假设该方法需要一个返回值,返回最后一个被复制的元素,则可以把上面方法改为如下形式:
    在这里插入图片描述
    在遍历 src 集合元素时,src 元素的类型是不确定的(只可以肯定它是 T 的子类),程序只能用 T 来笼统表示各种 src 集合的元素类型。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    使用这种语句,可以保证程序的 1 处调用后推断出最后一个被复制的元素类型是 Integer,而不是笼统的 NUmber 类型。
    实际上, Java 集合框架中的 TreeSet 有一个构造器也用到了这种设定通配符下限的语句,如下所示:
    在这里插入图片描述
    TreeSet 会对集合中的元素按自然顺序或定制顺序进行排序。如果需要 TreeSet 对集合中的所有元素进行定制排序,则要求 TreeSet 对象有一个与之关联的 Comparator 对象。上面构造器中的参数 c 就是进行定制排序的 Comparator 对象。
    Comparator 接口也是一个带泛型声明的接口:
    在这里插入图片描述
    通过这种带下限的通配符的语法,可以在创建 TreeSet 对象时灵活选择合适的 Comparator 。假定需要创建一个 TreeSet 集合,并传入一个可以比较 String 大小的 Comparator,这个 Comparator 既可以是 Comparator,也可以是 Comparator —只要尖括号里传入的类型是String 的父类型(或它本身)即可。
    在这里插入图片描述
    通过使用这种通配符下限的方式来定义 TreeSet 构造器的参数,就可以将所有可用的 Comparator 作为参数传入,从而增加了程序的灵活性。当然,不仅 TreeSet 有这种用法,TreeMap 也有类似的用法。

    5. 泛型方法与方法重载

    因为泛型既允许设定通配符的上限,也允许设定通配符的下限,从而允许在一个类里包含如下两个方法定义:
    在这里插入图片描述
    在这里插入图片描述
    上面的 MyUtils 类中包含两个 copy() 方法,这两个方法的参数列表有区别。
    在这里插入图片描述

    6. Java 8 改进的类型推断

    类型推断主要有如下两个方面:

    1. 可通过调用方法的上下文来推断类型参数的目标参数
    2. 可在方法调用链中,将推断得到的类型参数传递到最后一个方法。
      在这里插入图片描述
      上面程序中前两行粗体字代码的作用完全相同,但第一行代码无须在调用 MyUtil 类的 nil()方法时显式指定类型参数为 String ,这是因为程序需要将该方法的返回值赋值给 MyUtil类型,因此系统可以自动推断出此处的类型参数为 String 类型。
      上面程序中第 3 行与第 4 行粗体字代码的作用也完全相同,但第 3 行粗体字代码也无须在调用 MyUtil 类的 nil() 方法时显式指定类型参数为 Integer,这是因为程序将 nil() 方法的返回值作为了 MyUtil 类的 cons() 方法的第二个参数,而程序可以根据 cons() 方法的第一个参数(42)推断出此处的类型参数为 Integer 类型。
      虽然 Java 8 增强了泛型推断的能力,但也不是万能的。下面代码就是错的:
      在这里插入图片描述
      因此,上面这行代码必须显式指定类型参数,即将代码改为如下形式:
      在这里插入图片描述
    展开全文
  • java 泛型方法

    2019-11-15 15:17:49
    java泛型在方法的使用 Java泛型方法和类型通配符的区别 Java编程的逻辑 (36) - 泛型 (中) - 解析通配符
    展开全文
  • Java泛型方法

    千次阅读 2019-02-27 14:09:42
    Java泛型方法 1. 定义泛型方法 (1) 如果你定义了一个泛型(类、接口),那么Java规定,你不能在所有的静态方法、静态初块等所有静态内容中使用泛型的类型参数。例如: public class A&amp;amp;lt...

    Java泛型方法

    1. 定义泛型方法

    (1) 如果你定义了一个泛型(类、接口),那么Java规定,你不能在所有的静态方法、静态初块等所有静态内容中使用泛型的类型参数。例如:

    public class A<T> {
        public static void func(T t) {
        //报错,编译不通过
        }
    }
    

    (2) 如何在静态内容(静态方法)中使用泛型,更一般的问题是,如果类(或者接口)没有定义成泛型,但是就想在其中某几个方法中运用泛型(比如接受一个泛型的参数等),该如何解决?

    • 定义泛型方法就像定义泛型类或接口一样,在定义类名(或者接口名)的时候需要指定我的作用域中谁是泛型参数。例如:public class A<T> { ... }表明在类A的作用域中,T是泛型类型参数。
    • 定义泛型方法,其格式是:修饰符 <类型参数列表> 返回类型 方法名(形参列表) { 方法体 }。例如:public static <T, S> int func(List<T> list, Map<Integer, S> map) { ... },其中T和S是泛型类型参数。
    • 泛型方法的定义和普通方法定义不同的地方在于需要在修饰符和返回类型之间加一个泛型类型参数的声明,表明在这个方法作用域中谁才是泛型类型参数;
    • 不管是普通的类/接口的泛型定义,还是方法的泛型定义都逃不出两大要素:
      • 明哪些是泛型类型参数;
      • 这些类型参数在哪里使用。

    (3) 类型参数的作用域

    • class A<T> { ... }中T的作用域就是整个A;
    • public <T> func(...) { ... }中T的作用域就是方法func;

    • 类型参数也存在作用域覆盖的问题,可以在一个泛型模板类/接口中继续定义泛型方法,例如:

    class A<T> {
        // A已经是一个泛型类,其类型参数是T
        public static <T> void func(T t) {
        // 再在其中定义一个泛型方法,该方法的类型参数也是T
        }
    }
    //当上述两个类型参数冲突时,在方法中,方法的T会覆盖类的T,即和普通变量的作用域一样,内部覆盖外部,外部的同名变量是不可见的。
    //除非是一些特殊需求,一定要将局部类型参数和外部类型参数区分开来,避免发生不必要的错误,因此一般正确的定义方式是这样的:
    class A<T> {
        public static <S> void func(S s) {
        }
    } 
    

    (4) 泛型方法的类型参数可以指定上限,类型上限必须在类型参数声明的地方定义上限,不能在方法参数中定义上限。规定了上限就只能在规定范围内指定类型实参,超出这个范围就会直接编译报错。

    • <T extends X> void func(List<T> list){ ... },正确
    • <T extends X> void func(T t){ ... },正确
    • <T> void func(List<T extends X> list){ ... } ,编译错误

    2. 泛型调用

    (1) 显式指定方法的类型参数,类型参数要写在尖括号中并放在方法名之前。例如:object.<String> func(...),这样就显式指定了泛型方法的类型参数为String,那么所有出现类型参数T的地方都将替换成String类型。

    (2) 隐式地自动推断,不指明泛型参数,编译器根据传入的实参类型自动推断类型参数。例如:<T> void func(T t){ ... }隐式调用object.func("name"),根据"name"的类型String推断出类型参数T的类型是String

    (3) 避免歧义,例如:<T> void func(T t1, T t2){ ... }如果这样调用的话object.func("name", 15); 虽然编译不会报错,但是仍然会有很大隐患,T到底应该是String还是Integer存在歧义;

    (4) 有些歧义Java是会直接当成编译错误的,即所有和泛型参数有关的歧义,例如:<T> void func(List<T> l1, List<T> l2){...}如果这样调用的话,object.func(new List<String>(), new List<Integer>()); 这里会有歧义,编译器无法知道T到底应该是String还是Integer,这种歧义会直接报错的,编译无法通过。即泛型方法中,如果类型参数刚好就是泛型参数的类型实参,那么这个类型实参不得有歧义,否则直接编译报错。

    3. 泛型方法/类型通配符

    (1) 你会发现所有能用类型通配符(?)解决的问题都能用泛型方法解决,并且泛型方法可以解决的更好。

    • 类型通配符:void func(List<? extends A> list);
    • 完全可以用泛型方法完美解决:<T extends A> void func(List<T> list);

    (2) 两种方法可以达到相同的效果,“?”可以代表范围内任意类型,而T也可以传入范围内的任意类型实参,并且泛型方法更进一步,“?”泛型对象是只读的,而泛型方法里的泛型对象是可修改的,即List<T> list中的list是可修改的。

    (3) 两者最明显的区别

    • “?”泛型对象是只读的,不可修改,因为“?”类型是不确定的,可以代表范围内任意类型;
    • 而泛型方法中的泛型参数对象是可修改的,因为类型参数T是确定的(在调用方法时确定),因为T可以用范围内任意类型指定;

    (3) 适用场景

    • 一般只读就用“?”,要修改就用泛型方法。例如:
    public <T> void func(List<T> list, T t) {
        list.add(t);
    } 
    
    • 在多个参数、返回值之间存在类型依赖关系就应该使用泛型方法,否则就应该是通配符“?”。具体就是,如果一个方法的返回值、某些参数的类型依赖另一个参数的类型就应该使用泛型方法,因为被依赖的类型如果是不确定的"?",那么其他元素就无法依赖它。例如:<T> void func(List<? extends T> list, T t);即第一个参数依赖第二个参数的类型(第一个参数list的类型参数必须是第二个参数的类型或者其子类)。
    • <T, E extends T> void func(List<T> l1, List<E> l2); 这里E只在形参中出现了一次(类型参数声明不算),并且没有任何其他东西(方法形参、返回值)依赖它,那么就可以把E规约成“?”。规约结果<T> void func(List<T> l1, List<? extends T> l2);
    • 典型应用,容器赋值方法(Java的API):public static <T> void Collections.copy(List<T> dest, List<? extends T> src) { ... }从src拷贝到dest,那么dest最好是src的类型或者其父类,因为这样才能类型兼容,并且src只是读取,没必要做修改,因此使用“?”还可以强制避免对src做不必要的修改,增加的安全性。

    转自:https://www.cnblogs.com/csyuan/p/6908303.html
    展开全文
  • java泛型方法

    2017-01-20 11:48:44
    定义泛型方法语法格式如下:    调用泛型方法语法格式如下:    说明一下,定义泛型方法时,必须在返回值前边加一个,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的...
    
    

    定义泛型方法语法格式如下:

          

           调用泛型方法语法格式如下:

     

           说明一下,定义泛型方法时,必须在返回值前边加一个<T>,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值。

           Class<T>的作用就是指明泛型的具体类型,而Class<T>类型的变量c,可以用来创建泛型类的对象。

           为什么要用变量c来创建对象呢?既然是泛型方法,就代表着我们不知道具体的类型是什么,也不知道构造方法如何,因此没有办法去new一个对象,但可以利用变量c的newInstance方法去创建对象,也就是利用反射创建对象。

           泛型方法要求的参数是Class<T>类型,而Class.forName()方法的返回值也是Class<T>,因此可以用Class.forName()作为参数。其中,forName()方法中的参数是何种类型,返回的Class<T>就是何种类型。在本例中,forName()方法中传入的是User类的完整路径,因此返回的是Class<User>类型的对象,因此调用泛型方法时,变量c的类型就是Class<User>,因此泛型方法中的泛型T就被指明为User,因此变量obj的类型为User。

           当然,泛型方法不是仅仅可以有一个参数Class<T>,可以根据需要添加其他参数。

           为什么要使用泛型方法呢?因为泛型类要在实例化的时候就指明类型,如果想换一种类型,不得不重新new一次,可能不够灵活;而泛型方法可以在调用的时候指明类型,更加灵活。


    转自:http://www.cnblogs.com/iyangyuan/archive/2013/04/09/3011274.html

    展开全文
  • Java泛型方法和泛型类支持程序员使用一个方法指定一组相关方法,或者使用一个类指定一组相关的类型。 作用:使用Java泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、...
  • java 泛型方法使用示例

    热门讨论 2010-06-21 16:10:27
    java 泛型方法使用示例 java 泛型方法使用示例 java 泛型方法使用示例
  • 理解Java泛型方法

    2016-11-10 11:54:49
    理解Java泛型方法
  • 这里介绍的是Java泛型方法:主要是用于,一般情况下,我们在类定义上做泛型的声明,这样在整个类内部都可以使用泛型,有些情况下,我们不需要整个类做泛型声明,只希望在某个方法上用泛型,这时候就可以用泛型方法:...
  • 目录一、基本概述二、特性三、泛型的使用3.1 泛型类3.2 泛型接口3.3 泛型通配符3.4 泛型方法(1)泛型方法的基本用法(2)类中的泛型方法(3)泛型方法与可变参数(4)静态方法与泛型(5)泛型方法总结四、泛型数组 ...
  • JAVA泛型方法

    千次阅读 2009-03-25 11:27:00
    interface Collection { public boolean containsAll(Collection c);public boolean addAll(Collection c);// hey, type variables ...} 刚开始看到这样的方法定义时不知道方法返回类型前面的有什么用,其实这就是泛型
  • JAVA 泛型方法比较

    2012-08-21 10:29:00
    JAVA 泛型方法比较  ...java泛型方法在方法返回值是容器类对象时广泛使用。...一般来说编写java泛型方法时,返回值类型和至少一个参数类型应该是泛型,而且类型应该是一致的,如果只有返

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,221
精华内容 5,688
关键字:

java泛型方法

java 订阅