Java方法泛型、泛型接口和泛型方法怎么学习

  • 时间:
  • 5889人关注

这是一篇关于java相关的编程问答内容,被574位程序员关注,内容涉及到java方法泛型、Java教程、Java方法泛型教程等,由陈正浩编辑补充,一起来看下大家的回答。

Java方法泛型教程

泛型出现的动机在于:

有许多原因促成了泛型的出现,而最引人注意的一个原因,就是为了创建容器类。

泛型类

容器类应该算得上最具重用性的类库之一。先来看一个没有泛型的情况下的容器类如何定义:

public class Container{private String key;private String value;public Container(String k,String v){

Container类保存了一对key-value键值对,但是类型是定死的,也就说如果我想要创建一个键值对是String-Integer类型的,当前这个Container是做不到的,必须再自定义。那么这明显重用性就非常低。

当然,我可以用Object来代替String,并且在Java SE5之前,我们也只能这么做,由于Object是所有类型的基类,所以可以直接转型。但是这样灵活性还是不够,因为还是指定类型了,只不过这次指定的类型层级更高而已,有没有可能不指定类型?有没有可能在运行时才知道具体的类型是什么?

所以,就出现了泛型。

public class Container<K,V>{private K key;private V value;public Container(K k,V v){

在编译期,是无法知道K和V具体是什么类型,只有在运行时才会真正根据类型来构造和分配内存。如果你想学习Java可以来这个群,首先是五三二,中间是二五九,最后是九五二,里面有大量的学习资料可以下载。可以看一下现在Container类对于不同类型的支持情况:

public class Main{public static void main(String[]args){ 输出: name:findingsea

泛型接口

在泛型接口中,生成器是一个很好的理解,看如下的生成器接口定义:

public interface Generator<T>{public T next();

然后定义一个生成器类来实现这个接口:

public class FruitGenerator implements Generator<String>{

调用:

public class Main{public static void main(String[]args){

输出:

Banana

泛型方法

一个基本的原则是:无论何时,只要你能做到,你就应该尽量使用泛型方法。也就是说,如果使用泛型方法可以取代将整个类泛化,那么应该有限采用泛型方法。下面来看一个简单的泛型方法的定义:

public class Main{public static<T>void out(T t){

可以看到方法的参数彻底泛化了,这个过程涉及到编译器的类型推导和自动打包,也就说原来需要我们自己对类型进行的判断和处理,现在编译器帮我们做了。这样在定义方法的时候不必考虑以后到底需要处理哪些类型的参数,大大增加了编程的灵活性。

再看一个泛型方法和可变参数的例子:

public class Main{public static<T>void out(T...args){for(T t:args){

输出和前一段代码相同,可以看到泛型可以和可变参数非常完美的结合。

码农之家
精选回答2:Java泛型定义与java用法泛型实例详解

19小时59分钟前回答

今天码农之家java学院小编为大家介绍“Java泛型定义与java用法泛型实例详解”,此文结合实例形式较为详细的分析了Java中泛型的概念、原理、定义、使用方法及相关操作注意事项,希望通过此文能够帮助到那些有需要的小伙伴们,下面各位小伙伴们就随小编一起看看Java泛型定义与java用法泛型实例详解吧。

1、java泛型的由来

先看如下代码:

import java.util.List;

import java.util.ArrayList;

public class TestGeneric {

    @SuppressWarnings({ "rawtypes", "unchecked" })

    public static void main(String[] args) {

        List list = new ArrayList();

        list.add(1);

        list.add("1");

        list.add(new Object());

        System.out.println(list);

        // 取值

        Integer var1 = (Integer) list.get(0);

        String var2 = (String) list.get(1);

        Object var3 = list.get(2);

        System.out.println(var1 + " " + var2 + " " + var3);

    }

}


运行结果:


[1, 1, java.lang.Object@1db9742]

1 1 java.lang.Object@1db9742


这段代码很简单,将整形、字符串、对象放进list集合中,然后逐一取出。可以看出,由于List接口在定义时并不知道元素的类型,因此默认为Object,即任意类型元素进入list集合后都会自动装箱。而取值的过程更为复杂,所有取得的值都是装箱后的Object对象,必须得知道每一个元素的初始类型才能拆箱。一般使用集合的时候,集合的元素往往都是具有共同特征的,比如同属于一类的----那么,如果一开始限定了list集合元素的类型,那么就可避免上述不规范操作。代码如下:

import java.util.List;

import java.util.ArrayList;

public class TestGeneric {

    @SuppressWarnings("unused")

    public static void main(String[] args) {

        List<String> list = new ArrayList<String>();

        // list.add(1);//报错

        // list.add(new Object());//报错

        list.add("1");

        // 取值

        String var1 = list.get(0);// 无需转换

    }

}


如此一来,便有了泛型集合的说法。实际上,查阅List接口的Api会发现,List接口正是泛型接口,它可以接受一个类型参数E,若不传递参数,则默认是Object类型。


2、泛型类型的继承关系

有如下功能的代码,实现打印任意集合的元素:

import java.util.List;

import java.util.ArrayList;

import java.util.Collection;

public class TestGeneric{

    //打印任意集合元素

    public void print(Collection<Object> c){

     System.out.println(c);

    }

    public static void main(String[] args){

     List<String> list=new ArrayList<String>();

     new TestGeneric().print(list);

 }

}


输出:


TestGeneric.java:11: 无法将 TestGeneric 中的 print(java.util.Collection<java.lang.Object>) 应用于 (java.util.List<java.lang.String>)

   new TestGeneric().print(list);

                    ^

1 错误


很明显,意思就是传递的参数类型不匹配。难道String不是继承自Object的吗?没错,String是继承自Object的,但是List<String>List<Object>是截然不同的两个类型,两者之间没有任何继承关系。那如果真的要实现上面的功能,该如何呢?


(1)类型通配符


import java.util.List;

import java.util.ArrayList;

import java.util.Collection;

public class TestGeneric {

    // 打印任意集合元素

    public void print(Collection<?> c) {

        System.out.println(c);

    }

    public static void main(String[] args) {

        List<String> list = new ArrayList<String>();

        new TestGeneric().print(list);

    }

}


程序正常执行,这里的?表示一个未知类型,这个未知类型与Object不同,List<?>代表了所有的List<类型>的父类。


(2) 泛型方法

不只有通配符可以解决泛型继承的问题,若将上面的方法定义为泛型方法也具有同样的效果:

import java.util.List;

import java.util.ArrayList;

import java.util.Collection;

public class TestGeneric {

    // 打印任意集合元素

    public <T> void print(Collection<T> c) {

        System.out.println(c);

    }

    public static void main(String[] args) {

        List<String> list = new ArrayList<String>();

        new TestGeneric().print(list);

    }

}


泛型方法的定义形式如下:

修饰符 <T,E> 返回值 方法名(形参)

其中<T,E>在修饰符的后面做为类型定义,为方法指明形参中需要用到的T,E类型是来自哪里。既然泛型方法和类型通配符都可以实现泛型中的继承,那么有什么区别?

(3)泛型方法和通配符的区别


看如下代码:

import java.util.List;

import java.util.ArrayList;

import java.util.Collection;

public class TestGeneric {

    // 打印任意集合元素

    public <E, T extends E> void print(Collection<T> c1, Collection<E> c2) {

        System.out.println(c1);

        System.out.println(c2);

    }

    public static void main(String[] args) {

        List<Father> list1 = new ArrayList<Father>();

        List<Father> list2 = new ArrayList<Father>();

        new TestGeneric().print(list1, list2);// 传2个father类型

        List<Child> list3 = new ArrayList<Child>();

        List<Father> list4 = new ArrayList<Father>();

        new TestGeneric().print(list3, list4);// T为child,E为father

        List<Father> list5 = new ArrayList<Father>();

        List<Child> list6 = new ArrayList<Child>();

        new TestGeneric().print(list5, list6);// T为father,E为child,报错

    }

}

class Father {

}

class Child extends Father {

}

class Other {

}


上述泛型方法在定义T,E时已经指明了关系:T是E的子类,所以在传递参数的时候,T要么是E的子类,要么就是E本身,所以在传递关系不小心变为E exends T时,在第三次调用方法时报错了。而如果把上述代码换成?通配符的话,则不具有如此强的限定关系。

总之,泛型方法和?通配符都可以实现未知类型的继承,但是泛型方法主要强调多个未知类型之间的依赖关系。如果只是单纯用作成为一个通用类型的父类这一功能的话,两者都可以实现。

(4)泛型参数上、下限的注意

看如下代码:

import java.util.List;

import java.util.ArrayList;

import java.util.Collection;

public class TestGeneric {

    // 复制集合并返回原始集合的最后一个元素

    public <T> T copy(Collection<T> des, Collection<? extends T> src) {

        T lastElement = null;

        for (T t : src) {

            lastElement = t;

            des.add(t);

        }

        return lastElement;

    }

    public static void main(String[] args) {

        List<Number> des = new ArrayList<Number>();

        List<Integer> src = new ArrayList<Integer>();

        src.add(new Integer(1));

        Integer lastElement = new TestGeneric().copy(des, src);//

        System.out.println(lastElement.getClass());

    }

}


输出:


TestGeneric.java:18: 不兼容的类型

找到: java.lang.Number

需要: java.lang.Integer

Integer lastElement= new TestGeneric().copy(des,src);//

                                              ^

1 错误


当调用完copy方法后,系统比对发现T类型为Number,?类型为Integer。所以函数返回的T类型是Number了,所以根本不兼容Integer。要修改上面的代码,有2个办法,

方法1:

改为:Number lastElement=new TestGeneric().copy(des,src);

分析代码可以得出,?为T的子类,在方法中T=lastElement这句表现为多态,虽然返回的是T类型,但是多态的表现为?类型,即Interger类型,调用lastElement.getClass()也可发现返回的是java.lang.Integer类型,说明此处编译类型为T类型,实际运行类型为?类型。这就好比如下多态转换,

Father f=new Child();

Child c=f;//此处一定报错,类型不兼容

虽然f的多态表现为子类Child,但是上面一句连语法检测都过不了。这也就是为什么上面Integer不能兼容Number的原因了。

方法2:

改为:public <T> T copy(Collection<? super T> des,Collection<T> src)


这样一来,?类型变为了父类,T类型变为了子类,于是在方法中返回的T类型对象,即lastElement就不具有多态性了。泛型中的上下限是很有学问的,每次看源码时都会琢磨很久,但还是会在浩瀚的接口+泛型的设计中昏迷,这种设计真的完全是为了突出面向对象的特性。

从这也再次可以看出?通配符在处理具有依赖关系的泛型方法中,显得过于灵活而会导致一些潜在的隐患。

以上就是码农之家java学院小编介绍的“Java泛型定义与java用法泛型实例详解”的内容,希望通过此文能够帮助到大家,更多精彩内容请继续关注码农之家java学院官网。

展开问题

参考资料

  • Java EE互联网轻量级框架整合开发:SSM框架和Redis实现

    Java EE互联网轻量级框架整合开发:SSM框架和Redis实现

    随着移动互联网的兴起,以Java技术为后台的互联网技术占据了市场的主导地位,而在Java互联网后台开发中,SSM框架(Spring Spring MVC MyBatis)成为了主要架构,《 Java EE互联网轻量级框架整合开发

    大小:489.1 MBJavaEE

    立即下载
  • SOA与Java:用Java技术实现面向服务

    SOA与Java:用Java技术实现面向服务

    《SOA与Java:用Java技术实现面向服务》 几个创作者均是业内权威专家和领导者,总裁创作者ThomasErl也是SOA行业的领军人,编写过多本有关SOA的书籍,广火爆。这书详细介绍了应用Java技术性保持

    大小:173.8 MBJava技术

    立即下载
  • Java RESTful Web Service实战

    Java RESTful Web Service实战

    Java RESTful Web Service实战(第2版) 全书共10章,包括JAX-RS2入门、RESTAPI设计、REST请求处理、REST服务与异步、REST客户端、REST测试、微服务、容器化、JAX-RS调优、REST安全等内容。书中从基础概念开始

    大小:38.2 MBJava

    立即下载
  • Offer来了:Java面试核心知识点精讲(框架篇)

    Offer来了:Java面试核心知识点精讲(框架篇)

    《 Offer来了:Java面试核心知识点精讲(框架篇) 》是对Java程序员面试中常见的微服务、网络编程、分布式存储、分布式计算等必备知识点的总结,包括Spring原理与应用、SpringCloud原理与应用、Netty网络编程原理与应用、ZooKeeper原理与应用、Kafka原理与应用、Hadoop原理与应用、HBase原理与应用、Cassandra原理与应用、ElasticSearch原理与应用、Spark原理与应用、Flink原理与应用。 这本书有11章。第一章阐述Spring的原理和应用,涉及

    大小:218 MBJava面试

    立即下载
  • 实战Java高并发程序设计

    实战Java高并发程序设计

    大小:155793 MB Java

    立即下载

更多回答

充雨晴:26小时10分钟前回答

java中泛型是集合吗

泛型就是添加了一个类型参数你可以在用泛型类或者泛型方法的时候确定这个泛型为一个确定的类型 在以前的java版本中是没有泛型的只能用根类Object来表示泛型,但是这样的话就不能表示摸一个确定的类型因为object是所有类的父类所以它是一个表示所有类型 java中加入了泛型以后所有的集合框架都重新写了使它们支持泛型,这样你就可以这样写 ArrayListString al=new ArrayListString(); 表示一个String型的Arraylist 但是泛型有一个问题就是它不支持基本类型作为类型参数。 1、首先,了解一下Java关于泛型的概念。泛型,在C++中被称为模板,就是一种抽象的编程方式。当我们定义类和方法的时候,可以用一种通用的方……

莘嘉颖:44小时8分钟前回答

讲解最详细的Java泛型类的使用

对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下。本文参考java 泛型详解、Java中的泛型方法、 java泛型详解 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用。什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类……