15

I am new in Swift. I have a base class:

class foo{}

I want to implement a foo collection class:

class foos: Array<foo>{}

But the compiler is complaining:

inheritance from a non-protocol, non-class type of 'Array'

I have tried other syntax (e.g. [foo] and NSMutableArray<foo>) but none of them passes the compiler check.

This should be simple but I have googled all day and could not figure it out. Does anyone know if it is possible and if it is, the correct syntax?

1
  • There are some good answers here, but oftentimes you just want to add an extension. Unless you want to add additional state to the array, an extension is sufficient. Commented Jan 5, 2022 at 3:22

4 Answers 4

20

Swift's Array type is a structure, and in Swift, base classes must be actual classes (that is, class Foo) and not structures.

So you cannot do what you are trying to do via inheritance from Array, unfortunately. You could, however, store the array as a field within your class and forward methods to it, possibly implementing any protocols you want to support, et cetera.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, Josh. Did not realize Array is a structure. It is too bad I could not do what I want but what you suggested is the best I could do (A has B instead of A is B).
Even though it's impossible anyway... Generally speaking, there are a LOT of reasons to prefer composition to inheritance, especially when you don't own the base class. en.wikipedia.org/wiki/Composition_over_inheritance#Benefits
5

In Swift, Array is a struct, not a class. To have a class that is an array subclass, you will need to use NSArray, its Objective-C counterpart.

For example,

class Foo: NSArray{}

2 Comments

Thank you, erdekhayser. I want a generic type and NSArray is not a generic type.
Ironically, NSArray is actually generic as of recently.
3

In Swift 2.x you can use a protocol extension.

class Foo : Equatable {}
// you need to provide the Equatable functionality
func ==(leftFoo: Foo, rightFoo: Foo) -> Bool {
    return ObjectIdentifier(leftFoo) == ObjectIdentifier(rightFoo)
}

extension Array where Element : Foo {}

protocol extensions provide "insert points" to extend classes that aren't classes, classes you don't own, etc.

Comments

3

Details

  • Xcode 10.1 (10B61)
  • Swift 4.2

Idea

You can create own array using RangeReplaceableCollection protocol.

Sample

import Foundation

struct Arr<T: Equatable>: RangeReplaceableCollection {

    typealias Element = T
    typealias Index = Int
    typealias SubSequence = Arr<T>
    typealias Indices = Range<Int>
    fileprivate var array: Array<T>

    var startIndex: Int { return array.startIndex }
    var endIndex: Int { return array.endIndex }
    var indices: Range<Int> { return array.indices }


    func index(after i: Int) -> Int {
        return array.index(after: i)
    }

    init() { array = [] }

}

// Instance Methods

extension Arr {

    init<S>(_ elements: S) where S : Sequence, Arr.Element == S.Element {
        array = Array<S.Element>(elements)
    }

    init(repeating repeatedValue: Arr.Element, count: Int) {
        array = Array(repeating: repeatedValue, count: count)
    }
}

// Instance Methods

extension Arr {

    public mutating func append(_ newElement: Arr.Element) {
        array.append(newElement)
    }

    public mutating func append<S>(contentsOf newElements: S) where S : Sequence, Arr.Element == S.Element {
        array.append(contentsOf: newElements)
    }

    func filter(_ isIncluded: (Arr.Element) throws -> Bool) rethrows -> Arr {
        let subArray = try array.filter(isIncluded)
        return Arr(subArray)
    }

    public mutating func insert(_ newElement: Arr.Element, at i: Arr.Index) {
        array.insert(newElement, at: i)
    }

    mutating func insert<S>(contentsOf newElements: S, at i: Arr.Index) where S : Collection, Arr.Element == S.Element {
        array.insert(contentsOf: newElements, at: i)
    }

    mutating func popLast() -> Arr.Element? {
        return array.popLast()
    }

    @discardableResult mutating func remove(at i: Arr.Index) -> Arr.Element {
        return array.remove(at: i)
    }

    mutating func removeAll(keepingCapacity keepCapacity: Bool) {
        array.removeAll()
    }

    mutating func removeAll(where shouldBeRemoved: (Arr.Element) throws -> Bool) rethrows {
        try array.removeAll(where: shouldBeRemoved)
    }

    @discardableResult mutating func removeFirst() -> Arr.Element {
        return array.removeFirst()
    }

    mutating func removeFirst(_ k: Int) {
        array.removeFirst(k)
    }
    @discardableResult mutating func removeLast() -> Arr.Element {
        return array.removeLast()
    }

    mutating func removeLast(_ k: Int) {
        array.removeLast(k)
    }

    mutating func removeSubrange(_ bounds: Range<Int>) {
        array.removeSubrange(bounds)
    }

    mutating func replaceSubrange<C, R>(_ subrange: R, with newElements: C) where C : Collection, R : RangeExpression, T == C.Element, Arr<T>.Index == R.Bound {
        array.replaceSubrange(subrange, with: newElements)
    }

    mutating func reserveCapacity(_ n: Int) {
        array.reserveCapacity(n)
    }
}

// Subscripts

extension Arr {

    subscript(bounds: Range<Arr.Index>) -> Arr.SubSequence {
        get { return Arr(array[bounds]) }
    }

    subscript(bounds: Arr.Index) -> Arr.Element {
        get { return array[bounds] }
        set(value) { array[bounds] = value }
    }
}

// Operator Functions

extension Arr {

    static func + <Other>(lhs: Other, rhs: Arr) -> Arr where Other : Sequence, Arr.Element == Other.Element {
        return Arr(lhs + rhs.array)
    }

    static func + <Other>(lhs: Arr, rhs: Other) -> Arr where Other : Sequence, Arr.Element == Other.Element{
         return Arr(lhs.array + rhs)
    }

    static func + <Other>(lhs: Arr, rhs: Other) -> Arr where Other : RangeReplaceableCollection, Arr.Element == Other.Element {
        return Arr(lhs.array + rhs)
    }

    static func + (lhs: Arr<T>, rhs: Arr<T>) -> Arr {
        return Arr(lhs.array + rhs.array)
    }

    static func += <Other>(lhs: inout Arr, rhs: Other) where Other : Sequence, Arr.Element == Other.Element {
        lhs.array += rhs
    }
}

extension Arr: Equatable {
    static func == (lhs: Arr<T>, rhs: Arr<T>) -> Bool {
        return lhs.array == rhs.array
    }
}

extension Arr: CustomStringConvertible {
    var description: String { return "\(array)" }
}

Usage

// init
var array = Arr<Int>()
print(array)
array = Arr(repeating: 0, count: 5)
print(array)
array = Arr([1,2,3,4,5,6,7,8,9])
print(array)

// add
array.append(0)
print(array)
array.append(contentsOf: [5,5,5])
print(array)

// filter
array = array.filter { $0 < 7 }
print(array)

// map
let strings = array.map { "\($0)" }
print(strings)

// insert
array.insert(99, at: 5)
print(array)
array.insert(contentsOf: [2, 2, 2], at: 0)
print(array)

// pop
_ = array.popLast()
print(array)
_ = array.popFirst()
print(array)

// remove
array.removeFirst()
print(array)
array.removeFirst(3)
print(array)
array.remove(at: 2)
print(array)
array.removeLast()
print(array)
array.removeLast(5)
print(array)
array.removeAll { $0%2 == 0 }
print(array)
array = Arr([1,2,3,4,5,6,7,8,9,0])
array.removeSubrange(0...2)
print(array)
array.replaceSubrange(0...2, with: [0,0,0])
print(array)
array.removeAll()
print(array)

array = Arr([1,2,3,4,5,6,7,8,9,0])
print(array)

// subscript
print(array[0])
array[0] = 100
print(array)
print(array[1...4])


// operator functions
array = [1,2,3] + Arr([4,5,6])
print(array)
array = Arr([4,5,6]) + [1,2,3]
print(array)
array = Arr([1,2,3]) + Arr([4,5,6])
print(array)

1 Comment

Good on you @Vasily for actually putting a full implementation in there rather than just saying "don't do it"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.