Java泛型

1 什么是Java泛型

什么是Java泛型?看表面的意思,泛型就是指广泛的、普通的类型。在Java中是指把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型。 

看一个简单的例子:

List<String> strings = new ArrayList<String>();
strings.add("yiidian");
String aString = strings.get(0);

可以看到,通过菱形语法('<>')可以将List内元素的类型限定为String类型。
需要注意的是<>内的类型只能是引用类型,当然对于基本类型,可以使用对应的包装类型

2 Java泛型的好处

Java泛型主要有3个优点。如下:

2.1 类型安全

泛型只能存储一种类型的对象,它不允许存储其他对象,这样类型更加安全。

而没有泛型之前,我们可以存储任何类型的对象。

List list = new ArrayList();    
list.add(10);  
list.add("10");  
//使用泛型时,需要指定我们需要存储的对象的类型。 
List<Integer> list = new ArrayList<Integer>();    
list.add(10);  
list.add("10");// 编译错误

2.2 无需强制转换对象

在泛型之前,我们需要强制类型转换,使用了泛型后,我们无需强制类型转换了。

List list = new ArrayList();    
list.add("hello");    
String s = (String) list.get(0);//强制类型转换
//有了泛型,我们就不需要强制类型转换了
List<String> list = new ArrayList<String>();    
list.add("hello");    
String s = list.get(0);    

2.3 编译时检查

在编译时进行检查,因此在运行时不会出现问题。好的编程策略表明,在编译时处理问题比运行时要好得多。

List<String> list = new ArrayList<String>();    
list.add("hello");    
list.add(32);//编译错误

3 Java泛型的例子1

本案例我们使用ArrayList类,但是您可以使用任何集合类,例如ArrayList,LinkedList,HashSet,TreeSet,HashMap,Comparator等。

package com.yiidian;

/**
 * 一点教程网: http://www.yiidian.com
 */
/**
 * Java泛型的例子
 */
import java.util.*;

class Demo{
    public static void main(String args[]){
        ArrayList<String> list=new ArrayList<String>();
        list.add("eric");
        list.add("jack");
        //list.add(32);//编译错误

        String s=list.get(1);//无需类型强制转换
        System.out.println("element is: "+s);

        Iterator<String> itr=list.iterator();
        while(itr.hasNext()){
            System.out.println(itr.next());
        }
    }
}

输出结果为:

element is: jack
eric
jack

4 Java泛型的例子2

本案例我们将使用泛型使用map元素。在这里,我们需要传递key和value。让我们通过一个简单的例子来理解它:

package com.yiidian;

/**
 * 一点教程网: http://www.yiidian.com
 */
/**
 * Java泛型的例子
 */
import java.util.*;

class Demo{
    
    public static void main(String args[]){
        Map<Integer,String> map=new HashMap<Integer,String>();
        map.put(1,"eric");
        map.put(4,"jack");
        map.put(2,"rose");

        //现在将Map.Entry用于Set和Iterator
        Set<Map.Entry<Integer,String>> set=map.entrySet();

        Iterator<Map.Entry<Integer,String>> itr=set.iterator();
        while(itr.hasNext()){
            Map.Entry e=itr.next();//无需强制类型转换
            System.out.println(e.getKey()+" "+e.getValue());
        }

    }
}

输出结果为:

1 eric
2 rose
4 jack

5 Java泛型类

可以引用任何类型的类称为泛型类。在这里,我们使用T类型参数创建特定类型的泛型类。

让我们看一个创建和使用泛型类的简单示例。

5.1 创建一个泛型类

class MyGen<T>{  
   T obj;  
   void add(T obj){
     this.obj=obj;
   }  
   T get(){
     return obj;
   }  
}  

T类型表示它可以引用任何类型(例如String,Integer和Employee)。您为该类指定的类型将用于存储和检索数据。

5.2 使用泛型类

让我们看一下使用泛型类的代码。

package com.yiidian;

/**
 * 一点教程网: http://www.yiidian.com
 */
/**
 * 使用Java泛型类的例子
 */
class Demo{
    public static void main(String args[]){
        MyGen<Integer> m=new MyGen<Integer>();
        m.add(2);
        //m.add("eric");//编译错误
        System.out.println(m.get());
    }
}

输出结果为:

2

6 Java泛型方法

像泛型类一样,我们可以创建一个泛型方法,该方法可以接受任何类型的参数。参数的范围限于声明它的方法。它允许静态和非静态方法。

我们来看一个简单的Java泛型方法来打印数组元素的示例。我们在这里用E表示元素。

package com.yiidian;

/**
 * 一点教程网: http://www.yiidian.com
 */
/**
 * 使用Java泛型方法的例子
 */
public class Demo{

    public static < E > void printArray(E[] elements) {
        for ( E element : elements){
            System.out.println(element );
        }
        System.out.println();
    }

    public static void main( String args[] ) {
        Integer[] intArray = { 10, 20, 30, 40, 50 };
        Character[] charArray = { 'Y', 'I', 'I', 'D', 'I','A','N'};

        System.out.println( "打印Integer数组" );
        printArray( intArray  );

        System.out.println( "打印字符数组" );
        printArray( charArray );
    }
}

输出结果为:

打印Integer数组
10
20
30
40
50

打印字符数组
Y
I
I
D
I
A
N

7 Java泛型的类型参数

类型参数命名规范对于深入学习泛型很重要。常见的类型参数如下:

  1. T - Type
  2. E - Element
  3. K - Key
  4. N - Number
  5. V - Value

8 Java泛型的通配符

?(问号)符号表示通配符元素。它表示任何类型。如果我们写<?extends Number>,表示Number的任何子类,例如Integer,Float和double。现在我们可以通过任何子类对象调用Number类的方法。

我们可以使用通配符作为参数,字段,返回类型或局部变量的类型。但是,不允许将通配符用作泛型方法调用,泛型类实例创建或父类型的类型参数。

让我们通过以下示例了解泛型通配符:

package com.yiidian;

/**
 * 一点教程网: http://www.yiidian.com
 */
/**
 * 使用Java泛型通配符的例子
 */
import java.util.*;

abstract class Shape{
    abstract void draw();
}
class Rectangle extends Shape{
    void draw(){System.out.println("drawing rectangle");}
}
class Circle extends Shape{
    void draw(){System.out.println("drawing circle");}
}

class Demo{
    //创建一个仅接受Shape的子类的方法
    public static void drawShapes(List<? extends Shape> lists){
        for(Shape s:lists){
            s.draw();//通过子类实例调用Shape类的方法
        }
    }
    public static void main(String args[]){
        List<Rectangle> list1=new ArrayList<Rectangle>();
        list1.add(new Rectangle());

        List<Circle> list2=new ArrayList<Circle>();
        list2.add(new Circle());
        list2.add(new Circle());

        drawShapes(list1);
        drawShapes(list2);
    }
}

输出结果为:

drawing rectangle
drawing circle
drawing circle

8.1 上限通配符(< ? extends E>)

上限通配符的目的是减少对变量的限制。它将未知类型限制为特定类型或该类型的子类型。它通过声明通配符("?"),后跟extend(对于类而言)或implements(对于接口而言)关键字以及其上限来使用。

上限通配符的语法

List<? extends Number>
  • ?:是通配符。
  • extended:是一个关键字。
  • Number:是java.lang包中存在的类

假设我们要为Number及其子类型(如Integer,Double)的List编写方法。使用List<?extends Number>适用于Number类型或其任何子类的列表,而List<Number>仅适用于Number类型的列表。那么,List<?extends Number>的限制比List<Number>的限制少。

上限通配符的例子

在此示例中,我们使用上限通配符为List<Integer>和List<Double>编写方法。

package com.yiidian;

/**
 * 一点教程网: http://www.yiidian.com
 */
/**
 * 上限通配符的例子
 */
import java.util.ArrayList;

public class Demo {


    private static Double add(ArrayList<? extends Number> num) {

        double sum=0.0;

        for(Number n:num)
        {
            sum = sum+n.doubleValue();
        }

        return sum;
    }

    public static void main(String[] args) {

        ArrayList<Integer> l1=new ArrayList<Integer>();
        l1.add(10);
        l1.add(20);
        System.out.println("displaying the sum= "+add(l1));

        ArrayList<Double> l2=new ArrayList<Double>();
        l2.add(30.0);
        l2.add(40.0);
        System.out.println("displaying the sum= "+add(l2));


    }

}

输出结果为:

displaying the sum= 30.0
displaying the sum= 70.0

8.2 无限制通配符(?)

无限制通配符类型表示未知类型,例如List<>。这种方法在以下情况下很有用:

  • 通过使用Object类中提供的功能实现方法时。
  • 当泛型类包含不依赖于Type参数的方法时。

无限制通配符的例子

package com.yiidian;

/**
 * 一点教程网: http://www.yiidian.com
 */
/**
 * 无限制通配符的例子
 */
import java.util.Arrays;
import java.util.List;

public class Demo {

    public static void display(List<?> list)
    {

        for(Object o:list)
        {
            System.out.println(o);
        }

    }


    public static void main(String[] args) {

        List<Integer> l1=Arrays.asList(1,2,3);
        System.out.println("displaying the Integer values");
        display(l1);
        List<String> l2=Arrays.asList("One","Two","Three");
        System.out.println("displaying the String values");
        display(l2);
    }

}

输出结果为:

displaying the Integer values
1
2
3
displaying the String values
One
Two
Three

8.3 下限通配符(< ? super E>)

下限通配符的目的是将未知类型限制为特定类型或该类型的父类型。通过先声明通配符("?"),再声明super关键字,再声明其下限,来使用它。

下限通配符的语法

List<? super Integer>  
  • ?:是通配符。
  • super:是一个关键字。
  • Integer:是包装类。

假设我们要为Integer的List及其父类型(如Number,Object)编写方法。使用List<?super Integer>适用于Integer类型的List或其任何父类,而List<Integer>仅适用于Integer类型的List。那么,List<?super Integer>的限制比List<Integer>的限制少。 

下限通配符的例子

package com.yiidian;

/**
 * 一点教程网: http://www.yiidian.com
 */
/**
 * 下限通配符的例子
 */
import java.util.Arrays;
import java.util.List;

public class Demo {

    public static void addNumbers(List<? super Integer> list) {

        for(Object n:list)
        {
            System.out.println(n);
        }

    }
    public static void main(String[] args) {

        List<Integer> l1=Arrays.asList(1,2,3);
        System.out.println("displaying the Integer values");
        addNumbers(l1);

        List<Number> l2=Arrays.asList(1.0,2.0,3.0);
        System.out.println("displaying the Number values");
        addNumbers(l2);
    }

}

输出结果为:

displaying the Integer values
1
2
3
displaying the Number values
1.0
2.0
3.0

 

一点教程,一个分享编程知识的公众号。跟着站长一起学习和进步。

通俗易懂,深入浅出,一篇文章只讲一个知识点。

在公交、在地铁、在厕所都可以阅读,随时随地涨姿势。

文章不涉及代码,不烧脑细胞,人人都可以学习。

当你决定关注「一点教程」,你已然超越了90%的程序员!

一点教程二维码