What is cool about generics, why use them?

2019-01-01 15:10发布

I thought I'd offer this softball to whomever would like to hit it out of the park. What are generics, what are the advantages of generics, why, where, how should I use them? Please, keep it fairly basic. Thanks.

28条回答
初与友歌
2楼-- · 2019-01-01 15:35

I really hate to repeat myself. I hate typing the same thing more often than I have to. I don't like restating things multiple times with slight differences.

Instead of creating:

class MyObjectList  {
   MyObject get(int index) {...}
}
class MyOtherObjectList  {
   MyOtherObject get(int index) {...}
}
class AnotherObjectList  {
   AnotherObject get(int index) {...}
}

I can build one reusable class... (in the case where you don't want to use the raw collection for some reason)

class MyList<T> {
   T get(int index) { ... }
}

I'm now 3x more efficient and I only have to maintain one copy. Why WOULDN'T you want to maintain less code?

This is also true for non-collection classes such as a Callable<T> or a Reference<T> that has to interact with other classes. Do you really want to extend Callable<T> and Future<T> and every other associated class to create type-safe versions?

I don't.

查看更多
ら面具成の殇う
3楼-- · 2019-01-01 15:35

Don't forget that generics aren't just used by classes, they can also be used by methods. For example, take the following snippet:

private <T extends Throwable> T logAndReturn(T t) {
    logThrowable(t); // some logging method that takes a Throwable
    return t;
}

It is simple, but can be used very elegantly. The nice thing is that the method returns whatever it was that it was given. This helps out when you are handling exceptions that need to be re-thrown back to the caller:

    ...
} catch (MyException e) {
    throw logAndReturn(e);
}

The point is that you don't lose the type by passing it through a method. You can throw the correct type of exception instead of just a Throwable, which would be all you could do without generics.

This is just a simple example of one use for generic methods. There are quite a few other neat things you can do with generic methods. The coolest, in my opinion, is type inferring with generics. Take the following example (taken from Josh Bloch's Effective Java 2nd Edition):

...
Map<String, Integer> myMap = createHashMap();
...
public <K, V> Map<K, V> createHashMap() {
    return new HashMap<K, V>();
}

This doesn't do a lot, but it does cut down on some clutter when the generic types are long (or nested; i.e. Map<String, List<String>>).

查看更多
还给你的自由
4楼-- · 2019-01-01 15:37

Not needing to typecast is one of the biggest advantages of Java generics, as it will perform type checking at compile-time. This will reduce the possibility of ClassCastExceptions which can be thrown at runtime, and can lead to more robust code.

But I suspect that you're fully aware of that.

Every time I look at Generics it gives me a headache. I find the best part of Java to be it's simplicity and minimal syntax and generics are not simple and add a significant amount of new syntax.

At first, I didn't see the benefit of generics either. I started learning Java from the 1.4 syntax (even though Java 5 was out at the time) and when I encountered generics, I felt that it was more code to write, and I really didn't understand the benefits.

Modern IDEs make writing code with generics easier.

Most modern, decent IDEs are smart enough to assist with writing code with generics, especially with code completion.

Here's an example of making an Map<String, Integer> with a HashMap. The code I would have to type in is:

Map<String, Integer> m = new HashMap<String, Integer>();

And indeed, that's a lot to type just to make a new HashMap. However, in reality, I only had to type this much before Eclipse knew what I needed:

Map<String, Integer> m = new Ha Ctrl+Space

True, I did need to select HashMap from a list of candidates, but basically the IDE knew what to add, including the generic types. With the right tools, using generics isn't too bad.

In addition, since the types are known, when retrieving elements from the generic collection, the IDE will act as if that object is already an object of its declared type -- there is no need to casting for the IDE to know what the object's type is.

A key advantage of generics comes from the way it plays well with new Java 5 features. Here's an example of tossing integers in to a Set and calculating its total:

Set<Integer> set = new HashSet<Integer>();
set.add(10);
set.add(42);

int total = 0;
for (int i : set) {
  total += i;
}

In that piece of code, there are three new Java 5 features present:

First, generics and autoboxing of primitives allow the following lines:

set.add(10);
set.add(42);

The integer 10 is autoboxed into an Integer with the value of 10. (And same for 42). Then that Integer is tossed into the Set which is known to hold Integers. Trying to throw in a String would cause a compile error.

Next, for for-each loop takes all three of those:

for (int i : set) {
  total += i;
}

First, the Set containing Integers are used in a for-each loop. Each element is declared to be an int and that is allowed as the Integer is unboxed back to the primitive int. And the fact that this unboxing occurs is known because generics was used to specify that there were Integers held in the Set.

Generics can be the glue that brings together the new features introduced in Java 5, and it just makes coding simpler and safer. And most of the time IDEs are smart enough to help you with good suggestions, so generally, it won't a whole lot more typing.

And frankly, as can be seen from the Set example, I feel that utilizing Java 5 features can make the code more concise and robust.

Edit - An example without generics

The following is an illustration of the above Set example without the use of generics. It is possible, but isn't exactly pleasant:

Set set = new HashSet();
set.add(10);
set.add(42);

int total = 0;
for (Object o : set) {
  total += (Integer)o;
}

(Note: The above code will generate unchecked conversion warning at compile-time.)

When using non-generics collections, the types that are entered into the collection is objects of type Object. Therefore, in this example, a Object is what is being added into the set.

set.add(10);
set.add(42);

In the above lines, autoboxing is in play -- the primitive int value 10 and 42 are being autoboxed into Integer objects, which are being added to the Set. However, keep in mind, the Integer objects are being handled as Objects, as there are no type information to help the compiler know what type the Set should expect.

for (Object o : set) {

This is the part that is crucial. The reason the for-each loop works is because the Set implements the Iterable interface, which returns an Iterator with type information, if present. (Iterator<T>, that is.)

However, since there is no type information, the Set will return an Iterator which will return the values in the Set as Objects, and that is why the element being retrieved in the for-each loop must be of type Object.

Now that the Object is retrieved from the Set, it needs to be cast to an Integer manually to perform the addition:

  total += (Integer)o;

Here, a typecast is performed from an Object to an Integer. In this case, we know this will always work, but manual typecasting always makes me feel it is fragile code that could be damaged if a minor change is made else where. (I feel that every typecast is a ClassCastException waiting to happen, but I digress...)

The Integer is now unboxed into an int and allowed to perform the addition into the int variable total.

I hope I could illustrate that the new features of Java 5 is possible to use with non-generic code, but it just isn't as clean and straight-forward as writing code with generics. And, in my opinion, to take full advantage of the new features in Java 5, one should be looking into generics, if at the very least, allows for compile-time checks to prevent invalid typecasts to throw exceptions at runtime.

查看更多
还给你的自由
5楼-- · 2019-01-01 15:37

Single most reason is they provide Type safety

List<Customer> custCollection = new List<Customer>;

as opposed to,

object[] custCollection = new object[] { cust1, cust2 };

as a simple example.

查看更多
美炸的是我
6楼-- · 2019-01-01 15:38

Generics avoid the performance hit of boxing and unboxing. Basically, look at ArrayList vs List<T>. Both do the same core things, but List<T> will be a lot faster because you don't have to box to/from object.

查看更多
回忆,回不去的记忆
7楼-- · 2019-01-01 15:40

Haven't you ever written a method (or a class) where the key concept of the method/class wasn't tightly bound to a specific data type of the parameters/instance variables (think linked list, max/min functions, binary search, etc.).

Haven't you ever wish you could reuse the algorthm/code without resorting to cut-n-paste reuse or compromising strong-typing (e.g. I want a List of Strings, not a List of things I hope are strings!)?

That's why you should want to use generics (or something better).

查看更多
登录 后发表回答