(八)泛型

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

泛型类

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

public class Container {
    private String key;
    private String value;

    public Container(String k, String v) {
        key = k;
        value = v;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}
 

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) {
        key = k;
        value = v;
    }

    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public V getValue() {
        return value;
    }

    public void setValue(V value) {
        this.value = value;
    }
}
 

在编译期,是无法知道KV具体是什么类型,只有在运行时才会真正根据类型来构造和分配内存。可以看一下现在Container类对于不同类型的支持情况:

public class Main {

    public static void main(String[] args) {
        Container<String, String> c1 = new Container<String, String>("name", "findingsea");
        Container<String, Integer> c2 = new Container<String, Integer>("age", 24);
        Container<Double, Double> c3 = new Container<Double, Double>(1.1, 2.2);
        System.out.println(c1.getKey() + " : " + c1.getValue());
        System.out.println(c2.getKey() + " : " + c2.getValue());
        System.out.println(c3.getKey() + " : " + c3.getValue());
    }
}

输出:

name : findingsea
age : 24
1.1 : 2.2

泛型接口

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

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

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

public class FruitGenerator implements Generator<String> {

    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override
    public String next() {
        Random rand = new Random();
        return fruits[rand.nextInt(3)];
    }
}
 

调用:

public class Main {

    public static void main(String[] args) {
        FruitGenerator generator = new FruitGenerator();
        System.out.println(generator.next());
        System.out.println(generator.next());
        System.out.println(generator.next());
        System.out.println(generator.next());
    }
}

输出:

Banana
Banana
Pear
Banana

泛型方法

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

public class Main {

    public static <T> void out(T t) {
        System.out.println(t);
    }

    public static void main(String[] args) {
        out("findingsea");
        out(123);
        out(11.11);
        out(true);
    }
}

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

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

public class Main {

    public static <T> void out(T... args) {
        for (T t : args) {
            System.out.println(t);
        }
    }

    public static void main(String[] args) {
        out("findingsea", 123, 11.11, true);
    }
}
 
时间: 2024-12-09 16:28:10

(八)泛型的相关文章

爪哇国新游记之十八----泛型栈类

import java.lang.reflect.Array; /** * 泛型栈 * * @param <T> */ public class Stack<T>{ private Class<T> type;// 栈元素所属的类 private int size;// 栈深度 private T[] arr;// 用数组存储 private int top;// 栈顶元素的下标 public Stack(Class<T> type,int size){ t

C#基础入门 八

C#基础入门 八 泛型 C#中的泛型能够将类型作为参数来传递,即在创建类型时用一个特定的符号,如"T"来作为一个占位符,代替实际的类型,等待实例化时用一个实际的类型来代替. public class Test<T> { public void Swap(T a,T b) { } } 使用泛型类型可以最大限度的重用代码.保护类型的安全以及提高性能 降低了强制转换或装箱操作的成本或风险.可以对泛型进行约束以访问特定数据类型的方法 实例化:Test<int> test

C#泛型(Generic)

一.什么是泛型 泛型(Generic)是C#语言2.0.通用语言运行时(CLR)2.0..NET Framework2.0推出来的新特性. 泛型为.NET框架引入类型参数(Type Parameters)的概念.类型参数使得设计类和方法时,不必确定一个或多个参具体数. 具体的参数类型可延迟到声明和使用时再确定.避免了运行时类型转换或装箱操作的代价和风险. 二.泛型的使用和对比 2.1.CommandMethod(普通方法) 1 /// <summary> 2 /// 打印一个Int值 3 //

Node.js 网页瘸腿爬虫初体验

延续上一篇,想把自己博客的文档标题利用Node.js的request全提取出来,于是有了下面的初哥爬虫,水平有限,这只爬虫目前还有点瘸腿,请看官你指正了. // 内置http模块,提供了http服务器和客户端功能 var http=require("http"); // 内置文件处理模块 var fs=require('fs'); // 创建一个将流数据写入文件的WriteStream对象 var outstream=fs.createWriteStream('./1.txt'); /

跟王老师学泛型(八):泛型擦除与转换

泛型擦除与转换 主讲教师:王少华 QQ群:483773664 学习目标: 掌握泛型擦除的含义 理解泛型转换的规则 一.泛型擦除 (一)什么泛型擦除 Java中的泛型基本上都是在编译器这个层次来实现的.在生成的Java字节码中是不包含泛型中的类型信息的.也就是说虚拟机中没有泛型,只有普通类和普通方法 使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉.这个过程就称为类型擦除. 类型擦除的主要过程如下:     1.将所有的泛型参数用其最左边界(最顶级的父类型)类型替换.     2.移除所有

java从基础知识(八)泛型

1.什么是泛型? 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法. Java语言引入泛型的好处是安全简单. 在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的"任意化","任意化"带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的.对于强制类型转换错

Scala笔记整理(八):类型参数(泛型)与隐士转换

[TOC] 概述 类型参数是什么?类型参数其实就是Java中的泛型.大家对Java中的泛型应该有所了解,比如我们有List list = new ArrayList(),接着list.add(1),没问题,list.add("2"),然后我们list.get(1) == 2,对不对?肯定不对了,list.get(1)获取的其实是个String--"2",String--"2"怎么可能与一个Integer类型的2相等呢? 所以Java中提出了泛型的

泛型算法(八)之旋转算法

1.rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last):等效于循环左移序列,使得迭代器middle所指的元素成为首元素. std::vector<int> c; c.reserve(10); //向c中添加元素 for (int i = 0; i < 10; i++) { c.push_back(i); } //旋转c中的元素,使得数组下标为5的元素成为首元素 std::rotate(c.

十一、C# 泛型

为了促进代码重用,尤其是算法的重用,C#支持一个名为泛型的特性. 泛型与模块类相似. 泛型使算法和模式只需要实现一交.而不必为每个类型都实现一次.在实例化的时候,传入相应的数据类型便可. 注:可空值类型  也是基于泛型实现的. 泛型的本质 就是给某些复杂的类型,抽象一套数据类型的别名.然后可以在这复杂类型当中使用这些别名. 当运行时,可以通过为这一套抽象的数据类型指定实际的数据类型. 1.概述 利用泛型,可以在声明变量时创建用来处理特定类型的特殊数据结构.程序员定义这种参数化类型, 使特定泛型类