简体   繁体   English

Java泛型

[英]Generics in Java

I am having trouble to fully understand generics in Java. 我很难完全理解Java中的泛型。 I get the initial idea but I don't get it when it comes to more complicated implementations. 我得到了最初的想法,但是当涉及到更复杂的实现时我却不明白。 For example, in this code : 例如,在此代码中:

public class TokenCounterMapper 
     extends Mapper<Object, Text, Text, IntWritable>{

   private final static IntWritable one = new IntWritable(1);
   private Text word = new Text();

   public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
     StringTokenizer itr = new StringTokenizer(value.toString());
     while (itr.hasMoreTokens()) {
       word.set(itr.nextToken());
       context.write(word, one);
     }
   }
 }

Why do we have 4 generics parameters ( <Object, Text, Text, IntWritable> )? 为什么我们有4个泛型参数( <Object, Text, Text, IntWritable> )? What does they mean? 什么意思

There's nothing terribly complicated about this, but I'm not a fan of this example, so let's use another. 这没有什么复杂的,但是我不喜欢这个例子,所以让我们使用另一个例子。

Suppose we wanted to create a data structure that could hold any object. 假设我们想创建一个可以容纳任何对象的数据结构。 For that, we have an implementation of List : 为此,我们有一个List的实现:

public interface List {
    boolean add(Object data);
}

This does exactly what I want. 这正是我想要的。 Well, sort of. 好吧, 有点

What happens when I do this? 当我这样做时会发生什么? What should happen? 应该怎么办?

public static void main(String[] args) {
    List list = new ArrayList();

    list.add(10);
    list.add("20");
    list.add(30.0);

    for(int i = 0; i < list.size(); i++) {
        int item = (int) list.get(i);
        System.out.println(item);
    }
}

This is actually valid Java syntax, and will compile just fine. 这实际上是有效的Java语法,并且可以正常编译。 The issue here is that I will get an error at runtime - I can't cast a String to an int . 这里的问题是我在运行时会遇到错误-我无法将String转换为int

This is actually really bad - if I can't ensure type safety at compile time, then it could be quite a while before I understand the kind of casting error I've made. 这实际上是很糟糕的-如果我不能在编译时确保类型安全,那么可能要花相当长的一段时间才能了解我所犯的类型错误。 Worse, the issue may not manifest itself for a very long time. 更糟糕的是,这个问题可能不会在很长一段时间内显现出来。

Generics on a class allow you to specify a specific type to be bound to an instance of that class. 类的泛型允许您指定要绑定到该类实例的特定类型。 It also allows you to reuse code in the sense that you don't have to create a specific List implementation for String , Integer , Double , Float , and so forth. 在不必为StringIntegerDoubleFloat等创建特定的List实现的意义上,它还允许您重用代码。

Let's tweak this interface a bit: 让我们对该接口进行一些调整:

public interface List<T> {
    boolean add(T data);
}

The resultant code is all good news (and no, it shouldn't compile): 结果代码都是好消息(不,它不应该编译):

public static void main(String[] args) {
    List<Integer> list = new ArrayList<>();

    list.add(10);
    list.add("20");
    list.add(30.0);

    for(int i = 0; i < list.size(); i++) {
        int item = (int) list.get(i);
        System.out.println(item);
    }

}

First, I can get rid of the cast, which was the problem to begin with. 首先,我可以摆脱类型转换,这是开始要解决的问题。 Since I've type bound the entire list to Integer , I don't need to worry about casting it to an int . 由于我已经将整个列表绑定到Integer ,所以我不必担心将它强制转换为int Second, the error of my ways is made apparent to me at compile time - I'm adding stuff that isn't an Integer to my list! 其次, 在编译时 ,我的方法错误对我很明显-我要添加不是Integer That's most definitely a bug, and it's most definitely quick to fix. 那绝对是一个错误,而且绝对是最快速修复的。


Generics can compound themselves even further than this, which is why you often see generics going as high as 3 or 4 type parameters. 泛型甚至可以进一步自我组合,这就是为什么您经常会看到泛型高达3或4个类型参数的原因。 (If you have any more than that, you should really question if the class is doing too much.) Their purpose is twofold: (如果您还有其他功能,您应该真正质疑类是否做得太多。)它们的目的是双重的:

  • Ensure that the programmer doesn't have to do unnecessary casts to get back data that they put in. 确保程序员不必进行不必要的强制转换即可获取他们放入的数据。

  • Ensure that any errors in inserting incorrect data into the datatype or method is caught as early as possible - at compile time. 确保在编译时尽早发现将错误数据插入数据类型或方法中的任何错误。

There's an argument about readability , and in my experience, it really depends on what the class is responsible for, as well as the generics (and its bounds). 关于可读性一个争论 ,根据我的经验,它实际上取决于类负责什么,以及泛型(及其范围)。

As a lighter, slightly more intuitive example, explore the Guava Tables API. 作为一个更简单,更直观的示例,请探索Guava Tables API。 It's all row-key-value triplets, with the idea being that you have some object to represent a row value, some object to represent a key value, and some object that you actually care to store in the table. 所有这些都是行键值三元组,其想法是要有一些对象来代表行值,有一些对象来代表键值,还有一些对象实际上要存储在表中。

这样您的map()方法就知道它将接收什么类型的参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM