Generic classes
As in Java, classes in Kotlin may have type parameters:
class Box<T>(t : T) { var value = t }In general, to create an instance of such a class, one needs to provide the type parameters:
val box : Box<Int> = Box<Int>(1)But if the parameters may be inferred, e.g. from the constructor arguments or by some other means, one is allowed to omit the type parameters:
val box = Box(1) // 1 has type Int, so the compiler figures out that we are talking about Box<Int>Variance
One of the most tricky parts of Java's type system is wildcard types (see Java Generics FAQ). And Kotlin doesn't have any. Instead, it has two other things: declaration-site variance and type projections.
First, let's think about why Java needs those mysterious wildcards. The problem is explained in Effective Java, Item 28: Use bounded wildcards to increase API flexibility. First, generic types in Java are invariant, meaning that List<String> is not a subtype of List<Object>. Why so? If List was not invariant, it would have been no better than Java's arrays, cause the following code would have compiled and cause an exception at runtime:
So, Java prohibits such things in order to guarantee run-time safety. But this has some implications. For example, consider the addAll() method from Collection interface. What's the signature of this method? Intuitively, we'd put it this way:
But then, we would not be able to do the following simple thing (which is perfectly safe):
(In Java, we learned this lesson the hard way, see Effective Java's Item 25: Prefer lists to arrays.)
That's why the actual signature of addAll() is the following:
The wildcard type argument ? extends T indicates that this method accepts a collection of objects of some subtype of T, not T itself. This means that we can safely read T's from items (elements of this collection are instances of a subclass of T), but cannot write to it since we do not know what objects comply to that unknown subtype of T. In return for this limitation, we have the desired behaviour: Collection<String> is a subtype of Collection<? extends Object>. In "clever words", the wildcard with an extends-bound (upper bound) makes the type covariant.
The key to understanding why this trick works is rather simple: if you can only take items from a collection, then a collection of String's and reading Object's from it is fine. Conversely, if you can only put items into the collection, it's OK to take a collection of Object's and put String's into it: in Java we have List<? super String> a supertype of List<Object>. The latter is called contravariance, and you can only call methods that take String as an argument on List<? super String> (e.g., you can call add(String) or set(int, String)), while if you call something that returns T in List<T>, you don't get a String, but an Object.
Joshua Bloch calls those objects you only read from producers, and those you only write to – consumers. He recommends: "For maximum flexibility, use wildcard types on input parameters that represent producers or consumers", and proposes the following mnemonic:
PECS stands for producer-extends, consumer-super.
NOTE: if you use a producer-object, say, List<? extends Foo>, you are not allowed to call add() or set() on this object, but this does not mean that this object is immutable: for example, nothing prevents you to call clear() to remove all items from the list, since clear() does not take any parameters at all. The only thing guaranteed by wildcards (or other types of variance) is type safety. Immutability is a completely different story.
Declaration-site variance
Suppose we have a generic interface Source<T> that does not have any methods that take T as a parameter, only methods that return T:
Then, it would be perfectly safe to store a reference to an instance of Source<String> in a variable of type Source<Object> – there're no consumer-methods to call. But Java does not know this, and still prohibits:
To fix this, we have to declare objects of type Source<? extends Object> that is sort of meaningless, because we can call all the same methods on such a variable, as before, so there's no value added by the more complex type. But the compiler does not know that.
In Kotlin, there is a way to explain this sort of thing to the compiler. This is called declaration-site variance: we can annotate the type parameter T of Source to make sure that it is only returned (produced) from members of Source<T>, and never consumed. To do this we provide the out modifier:
abstract class Source<out T> { fun nextT() : T } fun demo(strs : Source<String>) { val objects : Source<Any> = strs // This is OK, since T is an out-parameter // ... }The general rule is: when a type parameter T of a class C is declared out, it may occur only in out-position in the members of C, but in return C<Base> can safely be a supertype of C<Derived>.
In "clever words" they say that the class C is covariant in the parameter T, or that T is a covariant type parameter. You can think of C as being a producer of T's, and NOT a consumer of T's.
The out modifier is called a variance annotation, and since it is provided at the type parameter declaration site, we talk about declaration-site variance. This is in contrast with Java's use-site variance where wildcards in the type usages make the types covariant.
In addition to out, Kotlin provides a complementary variance annotation: in. It makes a type parameter contravariant: it can only be consumed and never produced. A good example of a contravariant class is Comparable:
abstract class Comparable<in T> { fun compareTo(other : T) : Int } fun demo(x : Comparable<Number>) { x.compareTo(1.0) // 1.0 has type Double, which is a subtype of Number // Thus, we can assign x to a variable of type Comparable<Double> val y : Comparable<Double> = x // OK! }We believe that the word in and out are self-explainig (as they were successfully used in C# for quite some time already), thus the mnemonic mentioned above is not really needed, and one can rephrase it for a higher purpose:
The Existential Transformation: Consumer in, Producer out! ![]()
Use-site variance: Type projections
It is very convenient to declare a type parameter T as out and have no trouble with subtyping on the use site. Yes, it is, when the class in question can actually be restricted to only return T's, but what if it can't? A good example of this is Array:
class Array<T>(val length : Int) { fun get(index : Int) : T { /* ... */ } fun set(index : Int, value : T) { /* ... */ } }This class cannot be either co- or contravariant in T. And this imposes certain inflexibilities. Consider the following function:
fun copy(from : Array<Any>, to : Array<Any>) { assert {from.length == to.length} for (i in from.indices) to[i] = from[i] }This function is supposed to copy item from one array to another. Let's try to apply it in practice:
val ints : Array<Int> = array(1, 2, 3) val any = Array<Any>(3) copy(ints, any) // Error: expects (Array<Any>, Array<Any>)Here we run into the same familiar problem: Array<T> is invariant in T, thus neither of Array<Int> and Array<Any> is a subtype of the other. Why? Again, because copy might be doing bad things, i.e. it might attempt to write, say, a String to from, and if we actually passed an array of Int there, a ClassCastException would have been thrown sometime later...
Then, the only thing we want to ensure is that copy() does not do any bad things. We want to prohibit it to write to from, and we can:
fun copy(from : Array<out Any>, to : Array<Any>) { // ... }What has happened here is called type projection: we said that from is not simply an array, but a restricted (projected) one: we can only call those methods that return the type parameter T, in this case it means that we can only call get(). This is our approach to use-site variance, and corresponds to Java's Array<? extends Object>, but in a little simpler way.
You can project a type with in as well:
fun fill(dest : Array<in String>, value : String) { // ... }Array<in String> corresponds to Java's Array<? super String>, i.e. you can pass an array of CharSequence or an array of Object to the fill() function.
Star-projections
Sometimes you want to say that you know nothing about the type argument, but still want to use it in a safe way. The safe way here is to say that we are dealing with an out-projection (the object does not consume any values of unknown types), and that this projection is with the upper-bound of the corresponding parameter, i.e. out Any? for most cases. Kotlin provides a shortahnd syntax for this, that we call a star-projection: Foo<*> means Foo<out Bar> where Bar is the upperbound for Foo's type parameter.
Note: star-projections are very much like Java's raw types, but safe.
Generic functions
Not only classes can have type parameters. Functions can, too. Usually, one places the type parameters in angle brackets after the name of the function:
fun singletonList<T>(item : T) : List<T> { // ... }But for Extension functions it may be necessary to declare type parameters before specifying the receiver type, so Kotlin allows the alternative syntax:
fun <T> T.basicToString() : String { return typeinfo.typeinfo(this) + "@" + System.identityHashCode(this) }If type parameters are passed explicitly at the call site, they can be only specified after the name of the function:
val l = singletonList<Int>(1)Generic constraints
The set of all possible types that can be substituted for a given type parameter may be restricted by generic constraints.
Upper bounds
The most common type a constraint is an upper bound that corresponds to Java's extends keyword:
fun sort<T : Comparable<T>>(list : List<T>) { // ... }The type specified after a colon is the upper bound: only subtype of Comparable<T> may be substituted for T. For example
sort(list(1, 2, 3)) // OK. Int is a subtype of Comparable<Int> sort(list(HashMap<Int, String>())) // Error: HashMap<Int, String> is not a subtype of Comparable<HashMap<Int, String>>The default upper bound (if none specified) is Any?. There can be not more than one upper bound specified directly inside the angle brackets. If same type parameter needs more than one upper bound, we need a separate where-clause:
fun cloneWhenGreater<T : Comparable<T>>(list : List<T>, threshold : T) : List<T> where T : Cloneable { return list when {it > threshold} map {it.clone()} }Class objects
Another type of generic constraints are class object constraints. They restrict the properties of a class object of the root class of a type being substituted for T.
Consider the following example. Suppose, we have a class Default that has a property default that holds a default value to be used for this type:
abstract class Default<T> { val default : T }For example, the class Int could extend Default in the following way:
class Int { class object : Default<Int> { override val default = 0 } // ... }Now, let's consider a function that takes a list of nullable T's, i.e. T?, and replaces all the null's with the default values:
fun replaceNullsWithDefaults<T : Any>(list : List<T?>) : List<T> { return list map { if (it == null) T.default // For now, we don't know if T's class object has such a property else it } }For this function to compile, we need to specify a type constraint that requires a class object of T to be of a subtype of Default<T>:
fun replaceNullsWithDefaults<T : Any>(list : List<T?>) : List<T> where class object T : Default<T> { // ...Now the compiler knows that T (as a class object reference) has the default property, and we can access it.
| Class object bounds are not supported yet See the corresponding issue. |

44 Comments
comments.show.hideJul 20, 2011
Anonymous
Does it support higher-order polymorphism?
Jul 20, 2011
Anonymous
no
Jul 20, 2011
Anonymous
One of the biggest issues for me with generics is support for unboxed types because of overhead. Does Kotlin support generics on primitive types without boxing ?
Jul 20, 2011
Anonymous
or something like scala's @specialized?
Jul 20, 2011
Maxim Shafirov
Not for the first version, I'm afraid.
Oct 05, 2011
Anonymous
This example:
funcopy(from:Array<outAny>,to:Array<inAny>){...}Might be more instructive if it were written like this:
funcopy<T>(from:Array<outT>,to:Array<inT>){...}I assume that in this case, the compiler would infer T at the call site.
If you have:
valints:Array<Int>=array(1,2,3)valnumbers=Array<Number>(3)copy(ints,numbers)The compiler would infer T == Number.
Also, you might consider explaining completely how variance works in Kotlin without mentioning wildcards, at least as first. You could compare Kotlin's type variance with wildcards in an optional section toward the end.
I really like what you've done here--I find it a lot more intuitive than wildcards.
Jul 21, 2011
Andrey Breslav
Thanks
Jul 21, 2011
Anonymous
Are two instantiations of the same generic type truly and entirely separate? specifically:
1. Do they have the same shared state, or separate (in Java it would be statics, in Kotlin it is class objects)? If the same, then this means that type parameters cannot be used inside class objects?
2. Is it possible to inherit twice from the same generic type instantiated with different type parameters? i.e. "class Foo : List<Int>, List<String>"
Jul 21, 2011
Andrey Breslav
1. Yes, type parameters are not available in class objects
2. No
Jul 21, 2011
Anonymous
So they are not fully reified then. I think it's worth pointing this out - not because it's bad (most practical cases would be covered by what you support), but because it's different from languages with full reification, like C#.
Jul 21, 2011
Anonymous
(In plain text, w/o curly braces, this time.)
This example:
fun copy(from: Array<out Any>, to: Array<in Any>)
Might be more instructive if it were written like this:
fun copy<T>(from: Array<out T>, to: Array<in T>)
I assume that in this case, the compiler would infer T at the call site.
If you have:
val ints: Array<Int> = array(1, 2, 3)
val numbers = Array<Number>(3)
copy(ints, numbers)
The compiler would infer T == Number.
Also, you might consider explaining completely how variance works in Kotlin without mentioning wildcards, at least as first. You could compare Kotlin's type variance with wildcards in an optional section toward the end.
I really like what you've done here--I find it a lot more intuitive than wildcards.
-Bob Lee
Jul 21, 2011
Andrey Breslav
One thing with having a generic function there is that generic functions come later in the text.
And I agree that this text is a little bit too Java-oriented, we will probably fix this.
Jul 22, 2011
Anonymous
Reified generics is the most innovative idea in Kotlin.
But does it add any performance overhead? I think JVM doesn't support reified generics. Kotlin may have to maintain the type information at runtime.
Jul 22, 2011
Maxim Shafirov
Well, any generic class, like List<T> will have a reference to a TypeInfo object, so it'll occupy 4 to 8 bytes more memory. That's it
Sep 06, 2011
Anonymous
Are you saying that the List<T> class definition will be 4 to 8 bytes larger, or that every List<T> instance will be 4 to 8 bytes larger?
Sep 07, 2011
Andrey Breslav
Every instance.
Oct 31, 2012
Michael Hübner
It is not nessecary to add size to every instance.
can be transforemd into code
Variant 1:
The transformer checks if there is a "taylored" subclass of the raw generic class.
If not the following code is generated, saved in a *.java file and compiled:
The transformer first checks if there is a "binding accessing" interface for the generic class.
If not the following code is generated, saved in a *.java file and compiled:
Variant 2:
There is a common interface that is implemented by all taylored classes.
This simple approach works only in a program context where the binding type is fixed (e.g. String).
In a program context a type parameter may be bound to another type parameter:
Then the taylored classes must be created dynamically at run-time.
This can be done by a class e.g. BoundClassMap (implementation omitted here).
The transformed code then looks like this:
Oct 31, 2012
Andrey Breslav
What class loader will load that ArrayList_String?
Oct 31, 2012
Michael Hübner
I'm not an expert wrt possible ClassLoader issues.
I think the default class loader should be ok.
There is no risk of name clashed because the generated classes are in a separate package.
Oct 31, 2012
Andrey Breslav
We don't do this because of two problems:
Nov 11, 2012
Michael Hübner
In order to avoid problems with class loaders, synthetic subclasses are created instead of files in the file system according to:
http://www.javaranch.com/journal/200711/Journal200711.jsp#a4
Such a subclass implements the interface
The BoundClassMap creates such a synthetic subclass if a new combination of generic class and binding class(es) is required.
The instance method getClassName returns e.g.
"java.util.ArrayList<java.lang.String>".
This value is used in BoundClassMap to find the binding value.
Jul 22, 2011
Anonymous
I wonder how the following code would look like in Kotlin:
This code doesn't compile in Java, because the commandHandlers list is used both as a producer (line 15) and as a consumer (line 16), so the list should have been declared as both <? extends Base> as well as <? super Base>, which is not possible. In fact, it is impossible in Java to deal with this problem properly, without using @SuppressWarnings("unchecked").
Is there way to express it in Kotlin?
Jul 23, 2011
Andrey Breslav
This does not compile in Java for a different reason. The list is OK, the handler is the problem. Here's how Kotlin handles this with declaration-site variance:
abstractclassCommand{}classMyCommand():Command{}abstractclassCommandHandler<inT:Command>{funexec(command:T):Unit;}classMyCommandHandler:CommandHandler<MyCommand>{funexec(myCommand:MyCommand){}}funtest(){List<CommandHandler<Command>>commandHandlers=ArrayList<CommandHandler<Command>>();commandHandlers.add(MyCommandHandler());CommandHandler<Command>handler=commandHandlers[0];handler.exec(MyCommand());}Oct 29, 2012
Michael Hübner
The above add statement is NOT valid typed. This bug can be fixed by:
List<CommandHandler<in MyCommand>> commandHandlers
The principle of contravariance can then be shown by the assignment:
commandHandlers.add(CommandHandler());
Oct 30, 2012
Andrey Breslav
You are right, there's a mistake in the example. The correction is:
funtest(){valcommandHandlers:MutableList<CommandHandler<MyCommand>>=ArrayList();// MyCommand instead of CommandcommandHandlers.add(CommandHandler());//CommandHandler instead of MyCommandHandlervalhandler:CommandHandler<MyCommand>=commandHandlers[0];handler.exec(MyCommand());}Thanks.
P.S. "in" is not needed where you suggested it, because CommandHandler is already declared to be contravariant.
Sep 22, 2011
Anonymous
Would type projections allow me to have something like this on an immutable collection?
classCollection<outT>{funcontains(t:inT):boolean}That is, selectively allow some methods to act as "consumers" in an otherwise "producer" class?
Sep 22, 2011
Andrey Breslav
I don't see a way in which this can work.
Dec 27, 2011
Jan Novotný
Will there be a better support for SELF generic type? It's very handy for builder pattern. Java problem is described for example here:
http://calliopesounds.blogspot.com/2010/11/having-java-generic-class-return-type.html
Dec 28, 2011
Andrey Breslav
Yes. The type is called This.
Dec 28, 2011
Jan Novotný
Cool, can you please add some example of using This generics declaration? Or did I overlook it in the article?
Dec 28, 2011
Andrey Breslav
It's not there yet, because it's not yet implemented. I'll add a description as soon as the implementation is done.
Apr 05, 2012
Clayton Wohl
I am trying the exact example of use-site variance given on this page, and it doesn't seem to work:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.CharSequence; cannot be cast to [Ljava.lang.String;
Apr 05, 2012
Svetlana Isakova
It's a bug. Mind you post an issue to our tracker http://youtrack.jetbrains.com/issues/KT? Thanks.
Sep 09, 2012
Mark Platvoet
Since Kotlin classes contain more information at runtime it might be interesting to add one more, namely immutability information. I think it's a big plus if we could constrain the type parameter to only immutable classes, e.g.:
and of course:
This could be interesting for all sorts of frameworks build with/for Kotlin but mainly for concurrent libraries. Since (pure) immutable classes can safely be passed around among multiple threads it greatly reduces complexity.
I would define a pure immutable class as all of the properties are of type val and all properties are of immutable types
Granted, this wouldn't be number one on the feature list but it might be interesting in the Future<T:immutable>
Sep 10, 2012
Andrey Breslav
Thanks.
We are working on approaches to immutability in the language
Sep 21, 2012
Zalim Bashorov
Will compiler retain generic type information at runtime?
Sep 21, 2012
Andrey Breslav
For now we are postponing this feature.
Oct 30, 2012
Michael Hübner
The implicit use-site variance might get you into trouble.
Example (in Java/Kotlin mixed syntax):
In version 2 of the program an internal method that uses T as output type is needed.
I see no easier way as to change the declaration-side specification of T to in+out (= no-variance).
This would require refactoring the declaration-side variance (and implicit use-site variance) into explicit use-side variance, i.e. type expressions of form Aggregator<P> would become Aggregator<in P> where necessary for type correctness.
I suppose, this possible hassle was the motivation for the design decision to only provide explicit use-side variance in Java, which makes the declaration-side variance superfluous.
To avoid the described problem in Kotlin, the compiler should force explicit use-site variance, if this variance is needed for type compatibility of assignments on the use-site (even in the case of declaration-site variance).
The resulting code with explicit use-site variance is short and readable (thanks to the short 'in' and 'out' keywords).
Oct 30, 2012
Andrey Breslav
The case you are making is valid, but I don't believe it is worth polluting the call sites.
If someone declared their class to be contravariant, they meant something by it, and by changing variance they change the contract of that abstraction dramatically. It is much like deciding to extend a different base class/interface: it won't fit where it used to fit before, but there probably is a good reason for that. (Also our experience with such things shows they don't tend to happen often in practice.)
As far as I know, the reason for having use-site variance in Java was, ironic as it sounds, ease of understanding. Authors of this proposal thought that the mental model of wildcard types is easier to grasp than use-site variance (it is also more flexible, btw). Practice has shown otherwise later, but there's little we can do.
Oct 31, 2012
Michael Hübner
In which way are wildcard types more flexible?
If so, why not to treat the Kotlin use-site type expressions as a
short and nice syntax but with the same semantics as wildcard types?
Oct 31, 2012
Andrey Breslav
Wildcards are more flexible than declaration-site variance alone, because one is free to decide whether some particular occurrence of, say, List is co-, contra- or invariant. Kotlin's type projections are very closely analogous to wildcards (module recursive types).
Jan 16, 2013
Yuri Samsoniuk
Is there a way to declare generic with multiple bounds?
I.e.
Jan 16, 2013
Andrey Breslav
Yes, with where clause (see above)
about 11 hours ago
Hugo Wainwright
Is it possible to apply multiple where clauses to a class?