I have a generic class of the form:
class BaseClass<T> {
var prop: T
...
}
I then have multiple subclasses of the form:
class SubClassOne: BaseClass<SomeSubClass> {
...
}
class SubClassTwo: BaseClass<SomeOtherSubClass> {
...
}
Where the type parameters SomeSubClass and SomeOtherSubClass both inherit from a common base class SomeBaseClass.
I now want to define a variable to store instances of both SubClassOne and SubClassTwo. I have tried many possibilities:
var obj: BaseClass
var obj: BaseClass<SomeBaseClass>
var obj: BaseClass<Any>
But the first attempt results in the error Reference to generic type 'BaseClass' requires arguments in <...>, and the other two result in the error Cannot assign value of type 'SubClassOne' to type ... when trying to assign a value. I even tried to trick the Swift compiler into inferring the type for me by initializing an array:
var testArray = [SubClassOne(), SubClassTwo()]
But even this failed, resulting in the error Heterogeneous collection literal could only be inferred to [Any]; add explicit type annotation if this is intentional. Indeed, the only type annotation that successfully allows storage of both SubClasses is Any or AnyObject. Is it possible to store these instances with a more specific type? If not, why?
The reason it's important to do so is that I ultimately want to get the property prop from the stored variable obj. I am unable to do so if obj is stored as Any. I am also unable to simply cast it to SubClassOne or SubClassTwo because the method itself where I am trying to access the properties is a generic method, and which of SubClassOne or SubClassTwo to cast to depends on the generic type parameter of the method:
func castObj<T>(asType: T.Type) {
(self.obj as? T).prop
}
Which would be called as: castObj(asType: SubClassOne.self) or castObj(asType: SubClassTwo.self). However, we run into the same problem: the only generic type parameter constraint I can define that accepts both SubClassOne and SubClassTwo is Any, and then the Swift compiler complains: Value of type 'T' has no member 'prop'.
As a workaround I tried to define a protocol that encapsulates the desired property:
protocol HasProp {
var prop: SomeBaseClass { get }
}
Then I added this to the declaration of SubClassOne and SubClassTwo. However this resulted in still another error: Type 'SubClassOne' does not conform to protocol 'HasProp'. This confuses me as well, since SubClassOne and SubClassTwo both inherit prop from BaseClass<SomeSubClass> and so actually do conform to the protocol.
In summary:
Is it possible to store instances of SubClassOne and SubClassTwo with a more specific type that gives access to properties of BaseClass? If not, why?
Why do the SubClasses not conform to the protocol as expected?
How can I change the design to attain my desired behavior?
The problem is that at the moment the function castObj has no type constraints for its generic parameter, T. By giving a type constraint of BaseClass you should be fine, since BaseClass has both properties.
func castObj<T: BaseClass>(asType: T.Type) {
(self.obj as? T).propOne
(self.obj as? T).propTwo
}
In your example, the type of propTwo was common to both subclasses and the type of propOne was specialized. Make your design reflect that.
[was]
class BaseClass<T,U> {
var propOne: T
var propTwo: U
...
}
class SubClassOne: BaseClass<SomeSubClass, SomeClass> {}
class SubClassTwo: BaseClass<SomeOtherSubClass, SomeClass> {}
[could be]
class BaseClass<U> {
var propTwo: U
...
}
class SubClassOne<T>: BaseClass<SomeClass> {
var propOne: T
...
}
class SubClassTwo<T>: BaseClass<SomeClass> {
var propOne: T
...
}
The point is to keep common things in the base class and compose your specializations.
There's a fundamental misconception that SubclassOne and SubclassTwo are in the same inheritance hierarchy. Because of the generic type, they inherit from different base classes. You cannot mix and match them.
Think about it. With inheritance you should be able to use any subclass anywhere where you have the base class, so in your test example:
var testArray = [SubClassOne(), SubClassTwo()]
What type would the right hand side of the following expressions have to be?
testArray[0].prop = something
And this one
testArray[1].prop = something;
In SubClassOne, the type of prop is SomeSubClass and in SubClassTwo the type of prop must be SomeOtherSubClass.
The only way for you to get this to work is for prop to be declared as SomeBaseClass and that removes the necessity for BaseClass to be generic.
Edit
Why doesn't the protocol work?
The problem with the protocol is that you define the property as having the type of the base class but it is read/write. A property in an implementation of the protocol cannot fulfill the contract with a property that is specialised to one of the subclasses because other bits of code need to be able to assign any instance of the base class to the property.
protocol MyProtocol
{
var prop: BaseClass
}
struct MyImplementation: MyProtocol
{
var prop: SubClass
}
class BaseClass {}
class SubClass: BaseClass {}
class DifferentSubClass: BaseClass {}
var instance: MyProtocol = MyImplementation()
instance.prop = DifferentSubClass()
// Should be legal because the protocol says so but the type of prop in instance is SubClass.
Related
I am very confused around the concept of "metatype" in the Swift language.
Suppose I have:
class SomeClass {
class func callClassMethod() {
print("I'm a class method. I belong to my type.")
}
func callInstanceMethod() {
print("I'm an instance method. I belong to my type instance.")
}
}
According to the definition:
A metatype type refers to the type of any type, including class types,
structure types, enumeration types, and protocol types.
SomeClass is already a type called SomeClass, then what exactly is the type of SomeClass?
I can create a SomeClass.Type variable:
let var1 : SomeClass.Type = SomeClass.self
var1.doIt();//"I'm a class method. I belong to my type."
but I can also call the static/class function this way:
SomeClass.doIt();//"I'm a class method. I belong to my type."
Are they the same?
They are the same because the compiler guarantees that class names are unique (Swift is name spaced by module), so there is only one thing that is of SomeClass.Type and that is the class SomeClass. The meta type is often useful when you just want to pass the type of something to a function but you don't want to pass an instance. Codable does this for instance:
let decoded = try decoder.decode(SomeType.self, from: data)
If you could not pass the meta type here then the compiler could still infer the return type based on an annotation on the left, but it would be less readable:
let decoded: Sometype = try decoder.decode(data)
Some libraries do use the type inference style instead, although the Apple preference seems to be to use the meta type as its clearer what the right side of the assignment is on its own, without relying on type inference from the left side of the assignment.
I am trying to understand : "self, dynamicType, Type". I have this code :
class SomeClass {}
let cls : SomeClass.Type = SomeClass.self
let cls2 : SomeClass = SomeClass()
Are cls and cls2 the same thing ?
Can someone give some detail about the differences ? Thanks
No, cls and cls2 are different things. The easiest way to understand the difference will be to extend your example like this:
class SomeClass {
class func doIt() {
print("I'm a class method. I belong to my type.")
}
func doItOnlyIfInstanceOfThisType() {
print("I'm a instance method. I belong to my type instance.")
}
}
And now let's take your cls:
let cls : SomeClass.Type = SomeClass.self
cls.doIt()
That will print I'm a class method. I belong to my type.. But you cannot invoke this:
cls.doItOnlyIfInstanceOfThisType() // causes a compilation error, PRO TIP: actually you can use this method as a func property, but I'll add the explanation for this later
Let's take your cls2. The only visible method of it is doItOnlyIfInstanceOfThisType because it's an instance method (of this type).
let cls2 : SomeClass = SomeClass()
cls2.doItOnlyIfInstanceOfThisType()
So the difference between them is that cls is a type and cls2 is an instance of this type.
A little bit more knowledge about why SomeClass.self and SomeClass()?
The type of a class also exists in memory (it has for example its own methods), as a singleton representing the Type (not an instance of this type - that's something different).
If you call self on a Type like this SomeClass.self you will get a singleton instance representing the SomeClass Type.
SomeClass() invokes the init() method of SomeClass, a constructor that creates an instance of SomeClass.
PRO Tip
You can manipulate a Type instance function (like closures/blocks in ObjC). It's a generated class method. But you must pass an instance of the type that you take this method from as an argument, like this:
let myFunc :()->() = cls.doItOnlyIfInstanceOfThisType(cls2)
myFunc()
A metatype type refers to the type of any type, including class types, structure types, enumeration types, and protocol types.
You can use the postfix self expression to access a type as a value. For example, SomeClass.self returns SomeClass itself, not an instance of SomeClass. And SomeProtocol.self returns SomeProtocol itself, not an instance of a type that conforms to SomeProtocol at runtime. You can use a dynamicType expression with an instance of a type to access that instance’s dynamic, runtime type as a value, as the following example shows:
class SomeBaseClass {
class func printClassName() {
print("SomeBaseClass")
}
}
class SomeSubClass: SomeBaseClass {
override class func printClassName() {
print("SomeSubClass")
}
}
let someInstance: SomeBaseClass = SomeSubClass()
someInstance.dynamicType.printClassName()
// prints "SomeSubClass"
The compile-time type of someInstance is SomeBaseClass,
the runtime type of someInstance is SomeSubClass
You can use the identity operators (=== and !==) to test whether an instance’s runtime type is the same as its compile-time type.
if someInstance.dynamicType === SomeBaseClass.self {
print("The dynamic type of someInstance is SomeBaseCass")
} else {
print("The dynamic type of someInstance isn't SomeBaseClass")
}
More detail -> The Swift Programming Language: Types
Swift 3.0:
.dynamicType is deprecated, you should use type(of:) to get its meta type:
type(of: someInstance).printClassName()
I have this kind of code:
protocol MyProtocol {
}
class P1: MyProtocol {
}
class P2: MyProtocol {
}
class C <T: MyProtocol> {
}
Then i need to define a variable to delegate all kinds of C<MyProtocol>:
var obj: C <MyProtocol>
But compile error comes:
Using 'MyProtocol' as a concrete type conforming to protocol 'MyProtocol' is not supported
How can I do?
This code:
class C <T: MyProtocol> { }
Means that C is a generic class that can specialize on any type T that conforms to MyProtocol.
When you declare:
var obj: C <MyProtocol>
You are (I think) trying to say that the var obj will be an instance of C specialized to some type that conforms to MyProtocol,but you can't specialize a generic class on a protocol type, because there is no such thing as a direct concrete instance of a protocol. There can only be instances of a type conforming to the protocol. And there can theoretically be many different types that conform to the protocol. So that notation doesn't really tell the compiler which specific specialization of C to use.
This shouldn't be a problem though, because you can write things like:
var obj: C<P1> = C<P1>()
or
var obj = C<P2>() // type is inferred
And within your class C you can still treat any uses of T as conforming to MyProtocol. So I think this should give you everything you need, as long as you remember that an instance of a generic class must be specialized to a single specific concrete type, not a protocol which could represent many possible concrete types.
You usually don't need to declare type for a Swift's variable. The compiler can infer type in most cases. Also, for generic class, you should let the compiler figure out what the generic classes resolve to:
class C<T: MyProtocol> {
var value: T
init (value: T) {
self.value = value
}
}
var obj = C(value: P1()) // type: C<P1>
// or:
var obj = C(value: P2()) // type: C<P2>
In my swift project I have a case where I use protocol inheritance as follow
protocol A : class{
}
protocol B : A{
}
What Im trying to achieve next is declaring another protocol with associated type, a type which must inherit from protocol A. If I try to declare it as :
protocol AnotherProtocol{
associatedtype Type : A
weak var type : Type?{get set}
}
it compiles without errors but when trying to adopt AnotherProtocol in the following scenario:
class SomeClass : AnotherProtocol{
typealias Type = B
weak var type : Type?
}
compilation fails with error claiming that SomeClass does not conform to AnotherProtocol. If I understood this correctly it means that B
does not adopt A while Im trying to declare and asking you on how to declare an associated type which inherits from protocol A?
I made the above assumption based on fact that the following scenario compiles just fine
class SomeDummyClass : B{
}
class SomeClass : AnotherProtocol{
typealias Type = SomeDummyClass
weak var type : Type?
}
This is pretty interesting. It appears that once you constrain the type of an associatedtype in a given protocol, you need to provide a concrete type in the implementation of that protocol (instead of another protocol type) – which is why your second example worked.
If you remove the A constraint on the associated type, your first example will work (minus the error about not being able to use weak on a non-class type, but that doesn’t seem to be related).
That all being said, I can't seem to find any documentation in order to corroborate this. If anyone can find something to back this up with (or completely dispute it), I’d love to know!
To get your current code working, you can use generics. This will actually kill two birds with one stone, as both your code will now compile and you'll benefit from the increased type safety that generics bring (by inferring the type you pass to them).
For example:
protocol A : class {}
protocol B : A {}
protocol AnotherProtocol{
associatedtype Type : A
weak var type : Type? {get set}
}
class SomeClass<T:B> : AnotherProtocol {
typealias Type = T
weak var type : Type?
}
Edit: It appears the above solution won't work in your particular case, as you want to avoid using concrete types. I'll leave it here in case it's useful for anyone else.
In your specific case, you may be able to use a type erasure in order to create a pseudo concrete type for your B protocol. Rob Napier has a great article about type erasures.
It's a bit of a weird solution in this case (as type erasures are normally used to wrap protocols with associatedtypes), and it's also definitely less preferred than the above solution, as you have to re-implement a 'proxy' method for each method in your A & B protocols – but it should work for you.
For example:
protocol A:class {
func doSomethingInA() -> String
}
protocol B : A {
func doSomethingInB(foo:Int)
func doSomethingElseInB(foo:Int)->Int
}
// a pseudo concrete type to wrap a class that conforms to B,
// by storing the methods that it implements.
class AnyB:B {
// proxy method storage
private let _doSomethingInA:(Void)->String
private let _doSomethingInB:(Int)->Void
private let _doSomethingElseInB:(Int)->Int
// initialise proxy methods
init<Base:B>(_ base:Base) {
_doSomethingInA = base.doSomethingInA
_doSomethingInB = base.doSomethingInB
_doSomethingElseInB = base.doSomethingElseInB
}
// implement the proxy methods
func doSomethingInA() -> String {return _doSomethingInA()}
func doSomethingInB(foo: Int) {_doSomethingInB(foo)}
func doSomethingElseInB(foo: Int) -> Int {return _doSomethingElseInB(foo)}
}
protocol AnotherProtocol{
associatedtype Type:A
weak var type : Type? {get set}
}
class SomeClass : AnotherProtocol {
typealias Type = AnyB
weak var type : Type?
}
class AType:B {
// implement the methods here..
}
class AnotherType:B {
// implement the methods here..
}
// your SomeClass instance
let c = SomeClass()
// set it to an AType instance
c.type = AnyB(AType())
// set it to an AnotherType instance
c.type = AnyB(AnotherType())
// call your methods like normal
c.type?.doSomethingInA()
c.type?.doSomethingInB(5)
c.type?.doSomethingElseInB(4)
You can now use the AnyB type in place of using the B protocol type, without making it any more type restrictive.
I am trying to understand : "self, dynamicType, Type". I have this code :
class SomeClass {}
let cls : SomeClass.Type = SomeClass.self
let cls2 : SomeClass = SomeClass()
Are cls and cls2 the same thing ?
Can someone give some detail about the differences ? Thanks
No, cls and cls2 are different things. The easiest way to understand the difference will be to extend your example like this:
class SomeClass {
class func doIt() {
print("I'm a class method. I belong to my type.")
}
func doItOnlyIfInstanceOfThisType() {
print("I'm a instance method. I belong to my type instance.")
}
}
And now let's take your cls:
let cls : SomeClass.Type = SomeClass.self
cls.doIt()
That will print I'm a class method. I belong to my type.. But you cannot invoke this:
cls.doItOnlyIfInstanceOfThisType() // causes a compilation error, PRO TIP: actually you can use this method as a func property, but I'll add the explanation for this later
Let's take your cls2. The only visible method of it is doItOnlyIfInstanceOfThisType because it's an instance method (of this type).
let cls2 : SomeClass = SomeClass()
cls2.doItOnlyIfInstanceOfThisType()
So the difference between them is that cls is a type and cls2 is an instance of this type.
A little bit more knowledge about why SomeClass.self and SomeClass()?
The type of a class also exists in memory (it has for example its own methods), as a singleton representing the Type (not an instance of this type - that's something different).
If you call self on a Type like this SomeClass.self you will get a singleton instance representing the SomeClass Type.
SomeClass() invokes the init() method of SomeClass, a constructor that creates an instance of SomeClass.
PRO Tip
You can manipulate a Type instance function (like closures/blocks in ObjC). It's a generated class method. But you must pass an instance of the type that you take this method from as an argument, like this:
let myFunc :()->() = cls.doItOnlyIfInstanceOfThisType(cls2)
myFunc()
A metatype type refers to the type of any type, including class types, structure types, enumeration types, and protocol types.
You can use the postfix self expression to access a type as a value. For example, SomeClass.self returns SomeClass itself, not an instance of SomeClass. And SomeProtocol.self returns SomeProtocol itself, not an instance of a type that conforms to SomeProtocol at runtime. You can use a dynamicType expression with an instance of a type to access that instance’s dynamic, runtime type as a value, as the following example shows:
class SomeBaseClass {
class func printClassName() {
print("SomeBaseClass")
}
}
class SomeSubClass: SomeBaseClass {
override class func printClassName() {
print("SomeSubClass")
}
}
let someInstance: SomeBaseClass = SomeSubClass()
someInstance.dynamicType.printClassName()
// prints "SomeSubClass"
The compile-time type of someInstance is SomeBaseClass,
the runtime type of someInstance is SomeSubClass
You can use the identity operators (=== and !==) to test whether an instance’s runtime type is the same as its compile-time type.
if someInstance.dynamicType === SomeBaseClass.self {
print("The dynamic type of someInstance is SomeBaseCass")
} else {
print("The dynamic type of someInstance isn't SomeBaseClass")
}
More detail -> The Swift Programming Language: Types
Swift 3.0:
.dynamicType is deprecated, you should use type(of:) to get its meta type:
type(of: someInstance).printClassName()