40

I'm trying to derive my class from generic class:

class foo<T> {}
class bar : foo<Int> {}

But this code fails to compile with en error:

Classes derived from generic classes must also be generic

How to avoid this limitation? Is it possible?

ssgreg
  • 568
  • 1
  • 4
  • 8

5 Answers5

23

Ssreg,

Unfortunately this is official:

You can subclass a generic class, but the subclass must also be a generic class.

Let us hope Apple fixes this in a future version.

Meanwhile, let us see this as an opportunity to exploit aggregation instead of subclassing.

NOTE:

As a poor man's version of a solution, one could use typealias:

class foo<T> {}
class bar<Int> : foo<Int> {}
typealias Bar = bar<Int>

This way, the rest of the code can be written just as if Apple already fixed the matter.

Community
  • 1
  • 1
Jean Le Moignan
  • 22,158
  • 3
  • 31
  • 37
  • You appear to be quoting the documentation, can you confirm this? I cannot find that passage in your link (or under the Types section). I wonder if they changed the docs and possibly intent. – Chris Conover Aug 27 '14 at 20:15
  • Hey @chrisco, I just tried it in b6: no cigar, compiler complains. – Jean Le Moignan Aug 27 '14 at 21:25
  • Right, I noticed the same in B6. But the documentation though - was that a direct quote, because I don't see that passage now? – Chris Conover Aug 27 '14 at 21:30
  • Yes, I was a direct quote. And you're right, can't find it on the new doc... That's odd. – Jean Le Moignan Aug 28 '14 at 08:42
  • 4
    let's note that when you write `class bar : foo {}` the Int become a placeholder for the generic type, overriding the actual swift Int type. This will generate very funny error messages when you try to use the Int's properties inside the class.. It's better to use the hpique solution with unused generic type in the child class, and to bind the base class to the real Int type (or the desired one) – hariseldon78 Oct 15 '14 at 17:45
  • I don't believe this will work. Like @hariseldon78 says, Int is now a generic type. When I try `Bar<[MyClass]> : Foo<[MyClass]>` it complains about a missing "identifier to name generic parameter" – Andreas Jan 08 '15 at 14:45
  • 1
    Instead I'm doing: `class bar : foo {}` – maxdec Apr 28 '15 at 07:47
19

I had the same issue and I found a solution!

class SomeType {
}
class Foo<T> {
}
class Bar<T: SomeType>: Foo<T> {
}

Now you can do:

let bar = Bar()

This will default Bar's type to SomeType. The only drawback is that you can only do this for 'subclassable' types. So for Int this will not work.

Tom van Zummeren
  • 9,130
  • 12
  • 52
  • 63
  • This does work, but has a slight code smell to it. Nice find. – rfrittelli Feb 10 '15 at 16:46
  • Just wanted to build on your findings and suggest a possible edit. I had success by building on your answer to declare a class `public final class NonGenericSubclass {}`, then building on your example above, I'd declare `Bar` like so: `class Bar : Foo { ... }`. This way, I get all the benefits of `Foo` typed to int, but I can still initialize as if `Bar` were not generic. Also, because `NonGenericSubclass` is declared `final`, it can't be subclassed, so any class declaring a generic type can consider it non generic for all intents and purposes. Good find! – Logan May 29 '15 at 03:14
16

In Swift 2 it's possible to subclass a generic class:

class Something {}

class Base<T> {}

class Fancy : Base<Something> {}
ricardopereira
  • 11,118
  • 5
  • 63
  • 81
15

An alternative to the typealias solution is to take the documentation literally:

You can subclass a generic class, but the subclass must also be a generic class.

So we make subclass a generic class, but of another type that we don't use for anything.

class foo<T> {

    func doSomething(t:T) -> T {
        return t;
    }
}

class bar<S> : foo<Int> {

    override func doSomething(t:Int) -> Int {
        return t * t;
    }
}

let b : bar = bar<AnyObject>()
b.doSomething(5) // 25 instead of 5

I prefer this approach to using typealias because it doesn't pollute the namespace with an additional type. This is of particular importance if the subclass (bar) is meant to be public or internal.

Edit

The documentation no longer includes the phrase about the limitation of generic subclasses. Hopefully this means that Apple plans to change this sooner than later.

Community
  • 1
  • 1
hpique
  • 119,096
  • 131
  • 338
  • 476
  • Tested superficially with Beta 6. – hpique Aug 27 '14 at 01:50
  • it's funny how they design the language around the compiler limitations, instead of the viceversa.. – hariseldon78 Oct 15 '14 at 15:10
  • This seems to work, but tidying it up with `typealias bar = bar` won't – Andreas Jan 08 '15 at 15:18
  • I suggest creating the intermediate with another name so that you can use the typealias with the technique above.\n e.g.: class GenericBase {}\n class _ExtendBase : GenericBase { }\n typealias ExtendBase = _ExtendBase – Pat Niemeyer Apr 13 '15 at 04:29
2

This is now officially supported:

class Foo<RelatedType> {
    var object: RelatedType? = nil

    func myFunction(param: RelatedType) -> RelatedType? {
        return nil
    }
}

class Bar: Foo<String> {
    override func myFunction(param: String) -> String? {
        return self.object
    }
}
Garrett
  • 5,580
  • 2
  • 31
  • 47