10

I have a relatively complicated generic type (say Map<Long,Map<Integer,String>>) which I use internally in a class. (There is no external visibility; it's just an implementation detail.) I would like to hide this in a typedef, but Java has no such facility.

Yesterday I rediscovered the following idiom and was disappointed to learn that it's considered an anti-pattern .


class MyClass
{
  /* "Pseudo typedef" */
  private static class FooBarMap extends HashMap<Long,Map<Integer,String>> { };

  FooBarMap[] maps;

  public FooBarMap getMapForType(int type)
  {
    // Actual code might be more complicated than this
    return maps[type];
  }

  public String getDescription(int type, long fooId, int barId)
  {
    FooBarMap map = getMapForType(type);
    return map.get(fooId).get(barId);
  }

  /* rest of code */

}

Can there ever be any justification for this when the type is hidden and isn't forming part of a library API (which on my reading are Goetz's main objections to using it)?

McDowell
  • 107,573
  • 31
  • 204
  • 267
Simon Nickerson
  • 42,159
  • 20
  • 102
  • 127
  • 6
    You call that generic type complicated? Ha! When I were a lad we had to get up at 3 in the morning deal with six-level recursively nested generics... – Marcus Downing Aug 07 '09 at 08:40
  • 1
    Aye... with no shoes... uphill... in the snow.. – skaffman Aug 07 '09 at 08:49
  • 2
    @Marcus Downing: And the seventh-level you eat for breakfast at the restaurant at the end of the recursion. ;-) – Mnementh Aug 07 '09 at 10:36
  • New Archive link to the above IBM article: [Java theory and practice: The pseudo-typedef antipattern](https://web.archive.org/web/20170206190605/ibm.com/developerworks/java/library/j-jtp02216/index.html) – hellectronic Jul 08 '21 at 13:34

6 Answers6

13

IMO, the problem with Java anti-patterns is that they encourage black-and-white thinking.

In reality, most anti-patterns are nuanced. For example, the linked article explains how pseudo-typedefs leads to APIs whose type signatures are too restrictive, too tied to particular implementation decisions, viral, and so on. But this is all in the context of public APIs. If you keep pseudo-typedefs out of public APIs (i.e. restrict them to a class, or maybe a module), they probably do no real harm and they may make your code more readable.

My point is that you need to understand the anti-patterns and make your own reasoned judgement about when and where to avoid them. Simply taking the position that "I will never do X because it is an anti-pattern" means that sometimes you will rule out pragmatically acceptable, or even good solutions.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
5

The real problem is that this idiom creates an high coupling between your pseudo typedef and your client code. However since you are using FooBarMap privately there are no real problems of coupling (they are implementation details).

NB

A modern Java IDE should definitively helps to dealing with complicated generic types.

dfa
  • 114,442
  • 31
  • 189
  • 228
  • 2
    If you don't use this idiom, then you are creating a high coupling between the long, complicated type description (`HashMap>`) and client code. Could you explain why the antipattern's coupling is worse than that? – bacar Nov 20 '12 at 18:15
  • 1
    @bacar The issue is whether someone can use a `HashMap>` or your `FooMap`. To use an API that takes a `FooMap` you can't pass a HashMap. That is, they are only polymorphic in one direction if declared on an interface method. –  Nov 21 '12 at 01:48
  • The former argument reads like it should be preferred to use `byte[]` instead of `String` in many APIs, but `String` is chosen for a reason, to e.g. not need to think about character encoding and stuff everywhere always. One would never be allowed to user specialized maps in interfaces even if those specializations matter a lot as well, just think of sorted sets vs. those without any order. Creating some `FooMap` is perfectly fine to better describe structure and usage of types, make sure that people are really using the same type where expected, have one place to document that type etc. – Thorsten Schöning Sep 18 '19 at 09:00
3

For public interfaces I don't like to see generic types, because they have no meaning. To me seeing a method with an argument of HashMap<Long,Map<Integer,String>> is much like those C methods like foo(int, int, int, void*, int) and so on. Having a real type just makes code a lot easier to read. For a public interface it would be better to create FooBarMap that wraps HashMap<Long,Map<Integer,String>> rather than 'typedef,' but for class-internal use I see no down-side at all.

marijne
  • 2,992
  • 5
  • 22
  • 21
  • The problem with that approach is that the client code loses the usefulness of the Map data structure. – skaffman Aug 07 '09 at 08:50
0

Without this pseudo-typedef I would rather write

private Map<Long,Map<Integer,String>>[] maps;

and if I someday decide do change the implementing class from HashMap to TreeMap or Java7GreatEnhancedWonderfulMap :-) nothing will break. But with this, you are stuck with the HashMap. Well, you can make:

interface FooBarMap extends Map<Long,Map<Integer,String>> { };
class FooBarHashMap extends HashMap<Long,Map<Integer,String>> implements FooBarMap{ };

but it becomes more and more cumbersome.

Tadeusz Kopec for Ukraine
  • 12,283
  • 6
  • 56
  • 83
  • 3
    Not true because the type in the example is private, in fact the 'typedef' route saves some typing because you only have to replace Map -> HashMap in one place in the code. – marijne Aug 07 '09 at 08:15
  • If I declare a field with type Map, compiler won't let me call methods that are not declared in the interface. And if I substitute the implementing class with other class implementing Map, all the code will keep working. If I declare it as a HashMap, I can call HashMap methods. And substituting type in "typedef" will break the code. If I one day want to load my Map from DB using Hibernate, I can do it. If I have HashMap, I have to create new and copy the content of Map given by Hibernate. Generally using interfaces wherever it is possible is strongly encourages. This idiom discourages it. – Tadeusz Kopec for Ukraine Aug 07 '09 at 08:24
0

He has a good point about this when it comes to putting it in a public interface. This idiom is emulating some form of syntactic sugar, it should not affect what you show to your clients.

But I see no good reason for not using it locally, providing that it makes you life easier and code more readable - and you known what you are doing. Brian may generalize and advise strongly against it in any situation, but that's his Goetz feeling :p

Nicolas Simonet
  • 533
  • 4
  • 10
0

If you only use it in a small localised unit of code, the problems are small, true.

Thing is, so are the gains: your program probably won't even get textually smaller until you reference the pseudo-typedef 5+ times, which means the area it is visible in has to be non-trivial.

I probably wouldn't rewrite code that did this, but it seems a bad habit to get into.

soru
  • 5,464
  • 26
  • 30