Ever been stumped by <E>, <T>, <K, V> in OO language documentation?
Jermaine

Jermaine @creativ_bracket

About: Content Creator | Dart/Flutter Developer

Location:
London, UK
Joined:
Apr 21, 2018

Ever been stumped by <E>, <T>, <K, V> in OO language documentation?

Publish Date: Dec 27 '18
102 25

Over on the Reddit /r/dartlang group, an individual by the name of NFC_TagsForDroid reached out to me in regards to confusion navigating the Dart documentation. This was specifically when understanding the meaning behind some of the "tokens" used when demonstrating code samples.

Here's an extract from the user's comment:

Can you please write a explainer on how to read dartlang documentation? Most of it is meaningless to a beginner. For example: https://api.dartlang.org/stable/2.1.0/dart-core/List/add.html (Dart dart:core List<E> add abstract method)
the heading List<E> add abstract method what is the <E>? how does it being an abstract method affect anything?

The user is referring to the signature for the List type's add() method:

void add(
  E value
);
Enter fullscreen mode Exit fullscreen mode

The source of confusion is the E. Others used in various documentation code samples are T, K and V.

So what do these mean?

These seemingly magical "tagged letters" are placeholders that are used to represent what is known as a type parameter. This is common across statically-typed object-oriented languages and will be apparent when it comes to the topic of Generics.

Generics provide a way of telling the compiler what type is being used, so that it knows to check for that.

In other words if you see <E> read it as "of Type", so List<String> will be read as "List of String".

Now looking at that let's say we define a List<E>:

List<String> fruits = ['apple', 'orange', 'pineapple'];
Enter fullscreen mode Exit fullscreen mode

Looking at the add method again:

void add(
  E value
);
Enter fullscreen mode Exit fullscreen mode

The way to read this is that the E represents an element in the collection, whatever type we initially specified when we created that list! In the case of fruits its String.

And this is how we would use it:

fruits.add('mango');

fruits.add(1); // This will throw an error as it's the wrong type
Enter fullscreen mode Exit fullscreen mode

So why are particular letters used?

Simplest answer is convention. In fact you can use any letters you like, achieving the same effect. However the common ones carry semantic meaning:

  • T is meant to be a Type
  • E is meant to be an Element (List<E>: a list of Elements)
  • K is Key (in a Map<K, V>)
  • V is Value (as a return value or mapped value)

This code below will work even when I don't use any of the placeholder letters above. For example, see this snippet below:

class CacheItem<A> { // Using the letter A
  CacheItem(A this.itemToCache);

  final itemToCache;

  String get detail => '''
    Cached item: $itemToCache
    The item type is $A
  ''';
}

void main() {
  var cached = CacheItem<int>(1);
  print(cached.detail);
  // Output:
  // Cached item: 1
  // The item's type is int
}
Enter fullscreen mode Exit fullscreen mode

Run in DartPad

This works although the placeholder used is A. By convention T would be used:

class CacheItem<T> {
  CacheItem(T this.itemToCache);

  final itemToCache;

  String get detail => '''
    Cached item: $itemToCache
    The item type is $T
  ''';
}
Enter fullscreen mode Exit fullscreen mode

Generics are powerful in that they allow us to reuse the same class with various types:

CacheItem<String> cachedString = CacheItem<String>('Hello, World!');
print(cachedString.detail);
// Output:
// Cached item: Hello, World!
// The item's type is String

var cachedInt = CacheItem<int>(30);
print(cachedInt.detail);
// Output:
// Cached item: 30
// The item's type is int

var cachedBool = CacheItem<bool>(true);
print(cachedBool.detail);
// Output:
// Cached item: true
// The item's type is bool
Enter fullscreen mode Exit fullscreen mode

I hope this was insightful and you learnt something new today.

Further reading


Sharing is caring 🤗

If you enjoyed reading this post, please share this through the various social channels. Also, check out and subscribe to my YouTube channel (hit the bell icon too) for videos on Dart.

Subscribe to my email newsletter to download my Free 35-page Get started with Dart eBook and to be notified when new content is released.

Like, share and follow me 😍 for more content on Dart.

Comments 25 total

  • Jermaine
    JermaineDec 27, 2018

    I like this fix. Merged.

  • edA‑qa mort‑ora‑y
    edA‑qa mort‑ora‑yDec 28, 2018

    Nice practical intro.

    Just to add some terms, in case people wish to look further. The overall concept here are "parametric types" and "parametric functions", where "parameter" is referring to a type, as opposed to an "argument" which refers to a value to a function.

    The term "generics" generally refers to parametric container types, and some limited functions. It's a useful, but perhaps less-than-complete form of parametric types, as the implementations usually have many limitations. Full parametric allow a kind of meta-programming, C++ style. Whether this is better or not depends greatly on the experience of the programmer.

    Dynamically typed languages don't need parametric types as any container or function, can take any type at all. Though there's a related concept known as duck-typing that applies.

  • Leonardo Teteo
    Leonardo TeteoDec 28, 2018

    Good article for beginners and answering the question in the title, I don't remember any occasion I was stumped by that, I think it just clicked to me.

    • Jermaine
      JermaineDec 30, 2018

      Happy to see it just clicked. Took me a couple of attempts to grasp.

  • emptyother
    emptyotherDec 28, 2018

    Thanks! I've been using <T, U, V, ...> when i wrote generic types. Didn't put a second thought into which letters should be used (T for Type, U because T was taken, etc).

    Any reason why we don't use a multi-letter word, example: Record<KEY,VALUE> or List<ELEMENT>?

    • Jermaine
      JermaineDec 28, 2018

      I don't have a solid reason as to why multi-letter words are not used, apart from the fact that its more characters and therefore not conventional. Consequently, this could be mistaken for actual rather than placeholder types.

      • Gareth Bradley
        Gareth BradleyDec 31, 2018

        In C# you see multi element words, like TElement or TEntity for example.

        • Jermaine
          JermaineJan 1, 2019

          Yeah you're right. I had seen that in the docs. It's still got the placeholder letter as prefix:

          public interface ISessionChannel<TSession>
          {
              TSession Session { get; }
          }
          

          docs.microsoft.com/en-us/dotnet/cs...

  • Evaldas Buinauskas
    Evaldas BuinauskasDec 28, 2018

    V is there already. Also, read article again! It clearly says this 😉

    Simplest answer is convention. In fact you can use any letters you like, achieving the same effect.

  • David J Eddy
    David J EddyDec 28, 2018

    Great intro Jermaine!

  • Sandor Dargo
    Sandor DargoDec 28, 2018

    Thanks for the article. This reminds me of a code review I was part of.

    It was C++ code, involving some templates. Something like this:

    template <
        class C, // Something meaningful
        class R, // Something else meaningful
        class D, // Something explaning D well
        class H  // And again
    >
    class MyClass {
    // ... quite some quantity of code...
    };
    

    I asked the question if they thought about removing the comments and use meaningful names instead. The answer was: "yes we thought about it, but decided not to".

    Well...

    Should code read like well-written prose?

  • Ronaldo Geldres
    Ronaldo GeldresDec 28, 2018

    Thanks for the article :D.

    I have a question if you don't mind, What should I do when I have two types? Should I use T1 and T2?

    • Jermaine
      JermaineDec 29, 2018

      You would commonly do <T, U>, but really any letters can be used. I like the .NET constraints detailed here.

  • Jermaine
    JermaineDec 29, 2018

    Sure, you can do that.

  • Vince Ramces Oliveros
    Vince Ramces OliverosDec 30, 2018

    Remember Dart 2.1 has int-to-double conversion? I tried this with the Tween where T is dynamic, so its either 1.0 or 0.0 or a widget, and gave me an error to the flutter engine.

    For some cases, I still have doubts to use Generics.

  • flutterCode
    flutterCodeMar 28, 2019

    Good article.thx

  • Amin
    AminAug 13, 2019

    Great article, never really had the courage to deep dive into Dart but I'll do one day for sure! One thing that bothers me when writing things like

    class Cache<T> {}
    

    and then reading later, or in a comment

    T meaning Type
    

    Is that often when you say meaning, it really means that you could have just written it full length and not say that (I'm not talking about you personnally, just this kind of convention in general). Why not just write directly

    class Cache<CacheType> {
      public addItem(item: CacheType): void { /* ... */ }
      public getItems(): Array<CacheType> { /* ... */ }
    }
    

    So that it gets clearer for newcomers or people getting into this code someday. I like to think that my code is a form of art that another person will have to continue after me.

    • Jermaine
      JermaineAug 14, 2019

      Hey Amin,

      You can use multi-letter placeholders if you want, like the Microsoft docs, although they still prefixed the placeholder letters docs.microsoft.com/en-us/dotnet/cs...

      Personally I would be careful doing CacheType as that feels like there is a class CacheType somewhere, especially looking at addItem(item: CacheType)

      • Amin
        AminAug 15, 2019

        I see, hence the prefix like TCache. I never really understood that until now. Thanks Jermaine!

        • Jermaine
          JermaineAug 16, 2019

          You're welcome Amin.

Add comment