How can I check for generic type in Kotlin - generic-programming

I'm trying to test for a generic type in Kotlin.
if (value is Map<String, Any>) { ... }
But the compiler complains with
Cannot check for instance of erased type: jet.Map
The check with a normal type works well.
if (value is String) { ... }
Kotlin 0.4.68 is used.
What am I missing here?

The problem is that type arguments are erased, so you can't check against the full type Map, because at runtime there's no information about those String and Any.
To work around this, use wildcards:
if (value is Map<*, *>) {...}

I think this is more appropriate way
inline fun <reified T> tryCast(instance: Any?, block: T.() -> Unit) {
if (instance is T) {
block(instance)
}
}
Usage
// myVar is nullable
tryCast<MyType>(myVar) {
// todo with this e.g.
this.canDoSomething()
}
Another shorter approach
inline fun <reified T> Any?.tryCast(block: T.() -> Unit) {
if (this is T) {
block()
}
}
Usage
// myVar is nullable
myVar.tryCast<MyType> {
// todo with this e.g.
this.canDoSomething()
}

JVM removes the generic type information. But Kotlin has reified generics. If you have a generic type T, you can mark type parameter T of an inline function as reified so it will be able to check it at runtime.
So you can do:
inline fun <reified T> checkType(obj: Object, contract: T) {
if (obj is T) {
// object implements the contract type T
}
}

I'm gonna give a workaround solution but I think its clean, kind of
try{
(value as Map<String,Any>?)?.let { castedValue ->
doYourStuffHere() //using castedValue
}
}catch(e: Exception){
valueIsNotOfType() //Map<String,Any>
}

Here's what I use:
// Use this value if it is of type T, or else use defaultValue
inline fun <reified T> Any?.useIfTypeOrDefault(defaultValue: T) =
if (this is T) this else defaultValue
Usage (kotest):
val map = mapOf("foo" to listOf("cheese"), "bar" to 666)
map["foo"].useIfTypeOrDefault<List<String>>(emptyList()).firstOrNull() shouldBe "cheese"
map["bar"].useIfTypeOrDefault<List<String>>(emptyList()).firstOrNull() shouldBe null
map["foo"].useIfTypeOrDefault<Number>(-1) shouldBe -1
map["bar"].useIfTypeOrDefault<Number>(-1) shouldBe 666

I have tried the solution above with tryCast<Array<String?>> and, I guess, in my specific task in listing with many castings involved it was no so great idea, because it was slowing the performance drastically.
This is the solution I did finally - manually check the entries and call methods, like this:
fun foo() {
val map: Map<String?, Any?> = mapOf()
map.forEach { entry ->
when (entry.value) {
is String -> {
doSomeWork(entry.key, entry.value as String)
}
is Array<*> -> {
doSomeWork(entry.key, (entry.value as? Array<*>)?.map {
if (it is String) {
it
} else null
}?.toList())
}
}
}
}
private fun doSomeWork(key: String?, value: String) {
}
private fun doSomeWork(key: String?, values: List<String?>?) {
}

Related

Holding and releasing Closures in an array

I'm looking to make an observable property without relying on a reactive 3rd party lib / framework.
I read this and came up with a similar solution to their Observable Properties answer...
https://blog.scottlogic.com/2015/02/11/swift-kvo-alternatives.html
Theirs
class Observable<T> {
let didChange = Event<(T, T)>()
private var value: T
init(_ initialValue: T) {
value = initialValue
}
func set(newValue: T) {
let oldValue = value
value = newValue
didChange.raise(oldValue, newValue)
}
func get() -> T {
return value
}
}
Mine
public class Observable<V> {
public var value: V { didSet { for observer in observers { observer(value) } }}
private var observers = [(V) -> Void]()
public init(_ initital: V) {
value = initital
}
public func observe(with closure: #escaping (V) -> Void) {
observers.append(closure)
}
}
the only difference is I want to capture an array of closures instead of using Event and addHander... the reason being I want to provide the syntax of passing the value through rather than have the consumers of my code make a function every time and again to not rely on any 3rd party code.
I'm not sure how these closures could automatically be removed from the array once their owners are deallocated. I'm guessing they can't which is why addHandler is used, I'm just hoping someone out there more knowledgable than me can shed some light on the issue.
Thanks for your time.
So I come up with this solution:
class Wrapper<V> {
var observer: (V) -> Void
public init(_ b: #escaping (V) -> Void) {
observer = b
}
}
class Observable<V> {
public var value: V { didSet {
let enumerator = observers.objectEnumerator()
while let wrapper = enumerator?.nextObject() {
(wrapper as! Wrapper<V>).observer(value)
}
}}
private var observers = NSMapTable<AnyObject, Wrapper<V>>(keyOptions: [.weakMemory], valueOptions: [.strongMemory])
public init(_ initital: V) {
value = initital
}
public func observe(_ subscriber: AnyObject, with closure: #escaping (V) -> Void) {
let wrapper = Wrapper(closure)
observers.setObject(wrapper, forKey: subscriber)
}
}
The final API require subscriber to identify themselves at calling:
Observable.observe(self /* <-- extra param */) { /* closure */ }
Although we cannot weak ref a closure, but with NSMapTable, we can weak ref the subscriber object, then use it as a weak key to track observer closure. This allow deallocation of subscriber thus automatically cleanup outdated observers.
Finally, here's the code for a demo. Expand the snippet and copy-paste to swift playground and see it live.
import Foundation
func setTimeout(_ delay: TimeInterval, block:#escaping ()->Void) -> Timer {
return Timer.scheduledTimer(timeInterval: delay, target: BlockOperation(block: block), selector: #selector(Operation.main), userInfo: nil, repeats: false)
}
class Wrapper<V> {
var observer: (V) -> Void
public init(_ b: #escaping (V) -> Void) {
observer = b
}
}
class Observable<V> {
public var value: V { didSet {
let enumerator = observers.objectEnumerator()
while let wrapper = enumerator?.nextObject() {
(wrapper as! Wrapper<V>).observer(value)
}
}}
private var observers = NSMapTable<AnyObject, Wrapper<V>>(keyOptions: [.weakMemory], valueOptions: [.strongMemory])
public init(_ initital: V) {
value = initital
}
public func observe(_ subscriber: AnyObject, with closure: #escaping (V) -> Void) {
let wrapper = Wrapper(closure)
observers.setObject(wrapper, forKey: subscriber)
}
}
class Consumer {
private var id: String
public init(_ id: String, _ observable: Observable<Int>) {
self.id = id
observable.observe(self) { val in
print("[\(id)]", "ok, i see value changed to", val)
}
}
deinit {
print("[\(id)]", "I'm out")
}
}
func demo() -> Any {
let observable = Observable(1)
var list = [AnyObject]()
list.append(Consumer("Alice", observable))
list.append(Consumer("Bob", observable))
observable.value += 1
// pop Bob, so he goes deinit
list.popLast()
// deferred
setTimeout(1.0) {
observable.value += 1
observable.value += 1
}
return [observable, list]
}
// need to hold ref to see the effect
let refHolder = demo()
Edit:
As OP #Magoo commented below, the Wrapper object is not properly deallocated. Even though the subscriber object is successfully deallocated, and corresponding key is removed from NSMapTable, the Wrapper remain active as an entry held in NSMapTable.
Did a bit of test and found this is indeed the case, unexpectedly. Some further research reveal an unfortunate fact: it's a caveat in NSMapTable's implementation.
This post explain the reason behind thoroughly. Quote directly from Apple doc:
However, weak-to-strong NSMapTables are not currently recommended, as the strong values for weak keys which get zero’d out do not get cleared away (and released) until/unless the map table resizes itself.
Hmm, so basically Apple just think it's ok to keep them alive in memory until a resize takes place. Reasonable from GC strategy POV.
Conclusion: no chance it'd be handled if NSMapTables implementation remains the same.
However it shouldn't be a problem for most case. This Observer impl works as intended. And as long as the Wrapper don't do anything fishy and closure don't hold strong ref, only negative impact is just some extra memory footprint.
I do have a fix though, you can use weak -> weak map, so Wrapper as a weak value get dealloc too. But that'll require .observe() returns the Wrapper then Consumer get hold to a ref to it. I'm not keen on this idea, API not ergonomic to end user. I'd rather live with some memory overhead in favor of better API.
Edit 2:
I don't like the aforementioned fix cus the resulting API is not friendly. I saw no way around but #Magoo managed to NAIL IT! Using objc_setAssociatedObject API, which I never heard about before. Make sure to checkout his answer for detail, it's awesome.
OK so #hackape answer with objc_setAssociatedObject
public class Observable<V> {
private class ClosureWrapper<V> {
var closure: (V) -> Void
public init(_ closure: #escaping (V) -> Void) {
self.closure = closure
}
}
private var observers = NSMapTable<AnyObject, ClosureWrapper<V>>(keyOptions: [.weakMemory], valueOptions: [.weakMemory])
public var value: V { didSet { notify() } }
public init(_ initital: V) {
value = initital
}
public func addObserver(_ object: AnyObject, skipFirst: Bool = true, closure: #escaping (V) -> Void) {
let wrapper = ClosureWrapper(closure)
let reference = "observer\(UUID().uuidString)".replacingOccurrences(of: "-", with: "")
observers.setObject(wrapper, forKey: object)
// Giving the closure back to the object that is observing
// allows ClosureWrapper to die at the same time as observing object
objc_setAssociatedObject(object, reference, wrapper, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
if !skipFirst { closure(value) }
}
private func notify() {
let enumerator = observers.objectEnumerator()
while let wrapper = enumerator?.nextObject() { (wrapper as? ClosureWrapper<V>)?.closure(value) }
}
}
This guy also remade the NSMapTable in Swift using a really similar method https://codereview.stackexchange.com/questions/85709/generic-nsmaptable-replacement-written-in-swift
The easiest, and likely safest, solution would be use the exact implementation you have, but make sure all callers use [weak self] and guard that self still exists before performing any actions/side effects.
This way when the array of closures is executed, any that had their creator already dealloc will simply immediately return when called.
// called from outside class
observer.observe { [weak self] in
guard strongSelf = self else { return }
// do work using `strongSelf`
}
If the observer is going to be used by a lot of instances that are constantly deallocating, I'd recommend adding a remove observer function. To do this you'd probably want to return a string on the observe call and then use it for removing the closure. Something along the lines of this:
public typealias ObserverIdentifier = String
public class Observable<V> {
public var value: V { didSet { for observer in observers.values { observer(value) } }}
private var observers = [ObserverIdentifier : (V) -> Void]()
public init(_ initital: V) {
value = initital
}
#discardableResult public func observe(with closure: #escaping (V) -> Void) -> ObserverIdentifier {
let identifier = UUID().uuidString
observers[identifier] = closure
return identifier
}
public func remove(identifier: ObserverIdentifier) {
observers.removeValue(forKey: identifier)
}
}
Because you are using [weak self], removing the observer on deallocation is simply a nice thing to do to avoid some additional no-ops, but still completely safe if not removed.

Checking if a metatype is an Array with an Element type of kind MyProtocol

I need do different things for different classes. For example I've created the protocols
protocol ResponseProtocol {
associatedtype ResponseType: Any
}
protocol MappableProtocol {
init(map: String)
}
and I'm adding my data class MyDto
class MyDto: MappableProtocol {
required init(map: String) { }
}
and 3 different response classes
class A1: ResponseProtocol {
typealias ResponseType = String
}
class A2: ResponseProtocol {
typealias ResponseType = MyDto
}
class A3: ResponseProtocol {
typealias ResponseType = [MyDto]
}
now I need do different things depending on ResponseType.
I tried this code, but I'm running into problems with Arrays
class API {
func test<T: ResponseProtocol>(a: T) -> String {
if T.ResponseType.self is String.Type {
return "String"
}
if T.ResponseType.self is MappableProtocol.Type {
return "MappableProtocol"
}
if T.ResponseType.self is [Any].Type {
return "Array<Any>" // Why is this false?
}
if T.ResponseType.self is [MappableProtocol].Type {
return "Array<MappableProtocol>" //Why is this false?
}
if T.ResponseType.self is [MyDto].Type {
return "Array<MyDto>" // Why is only this true?
}
return "notFound"
}
}
let api = API()
let t1 = api.test(a: A1())
let t2 = api.test(a: A2())
let t3 = api.test(a: A3())
In my playground console, for array A3, I see Array<MyDto>, but I expected the first return for array like Array<Any>.
How can I check for an Array where the Element is of kind MappableProtocol?
The problem is that while instances of [MyDto] can be freely converted to [MappableProtocol] and [Any], these are really just magical conversions that the compiler does behind the scenes (see this Q&A for more information).
The same conversions don't exist for metatype values, which is why Swift says that a [MyDto].Type is not a [MappableProtocol].Type nor a [Any].Type – they are unrelated metatype types.
Likely the simplest solution in your case would just be to forget working with metatypes, and instead just declare different overloads of test(a:) to handle different ResponseType types.
// 'default' overload of test(a:)
func test<T : ResponseProtocol>(a: T) -> String {
return "notFound"
}
func test<T : ResponseProtocol>(a: T) -> String where T.ResponseType == String {
return "String"
}
func test<T : ResponseProtocol>(a: T) -> String where T.ResponseType : MappableProtocol {
return "MappableProtocol"
}
func test<T : ResponseProtocol>(a: T) -> String where T.ResponseType == [MyDto] {
return "Array<MDto>"
}
// overload of test(a:) that accepts a type that conforms to ResponseProtocol, where the
// ResponseType is an Array with arbitrary Element type.
func test<T : ResponseProtocol, ResponseTypeElement : MappableProtocol>(a: T) -> String
where T.ResponseType == [ResponseTypeElement]
{
return "Array<MappableProtocol>"
}
print(test(a: A1())) // String
print(test(a: A2())) // MappableProtocol
print(test(a: A3())) // Array<MyDto>
// A MappableProtocol with a ResponseType that conforms to MappableProtocol,
// but isn't MyDto.
class Foo : MappableProtocol { required init(map: String) { } }
class A4 : ResponseProtocol { typealias ResponseType = [Foo] }
print(test(a: A4())) // Array<MappableProtocol>
(I removed your API class just to simplify things)
The compiler will simply resolve which overload to call at compile time, rather than having the runtime jump through lots of type-casting hoops.
If you insist on working with metatype values, one possible solution is to define a dummy protocol for Array to conform to (see for example this similar Q&A), which we can then cast the metatype values to. We can then declare an elementType static requirement in order to extract the Array's Element.self metatype value, which we can then inspect the type of in order to determine what the array is convertible to.
For example, if we define and conform Array to _ArrayProtocol:
protocol _ArrayProtocol {
static var elementType: Any.Type { get }
}
extension Array : _ArrayProtocol {
static var elementType: Any.Type {
return Element.self
}
}
We can now use test(a:) like so:
func test<T : ResponseProtocol>(a: T) -> String {
if T.ResponseType.self is String.Type {
return "String"
}
if T.ResponseType.self is MappableProtocol.Type {
return "MappableProtocol"
}
// attempt to cast the T.ResponseType.self metatype value to the existential metatype
// type _ArrayProtocol.Type (i.e a type that conforms to _ArrayProtocol),
// in this case, that's only ever Array.
if let responseType = T.ResponseType.self as? _ArrayProtocol.Type {
// switch on the element type, attempting to cast to different metatype types.
switch responseType.elementType {
case is MyDto.Type:
return "Array<MyDto>"
case is MappableProtocol.Type:
return "Array<MappableProtocol>"
default:
return "Array<Any>"
}
}
return "notFound"
}
print(test(a: A1())) // String
print(test(a: A2())) // MappableProtocol
print(test(a: A3())) // Array<MyDto>
print(test(a: A4())) // Array<MappableProtocol>

How to check if an object is in array

Edit: The problem is already solved by #vacawama. But if you are looking for an answer for NSObject classes, you should implement isEqual function which is NSObjectProtocol. Otherwise you gonna get an error says: " Redundant conformance of 'classname' to protocol 'Equatable' "
You can check this for details: Swift 2.2, Contains Method not working
In swift, how can i check if an object is in array?
I have a simple class like this;
class Test: {
private var _number: Int!
private var _type: String!
var number: Int {
return _number
}
var type: String {
return _type
}
init (number: Int, type: String) {
self._number = number
self._type = type
}
}
Also i have this class;
class TestRandom {
private let _numberArr: [Int] = [1,2,3,4,5,6,7,8,9,10]
private let _typeArr: [String] = ["x","y","z"]
public private(set) var _testArr: [Test] = []
private var _randomTest: Test!
func randomTestPicker () {
repeat {
let randomNumber = Int(arc4random_uniform(UInt32(self._numberArr.count)))
let randomType = Int(arc4random_uniform(UInt32(self._typeArr.count)))
self._randomTest = Test(number: self._numberArr[randomNumber], type: self._typeArr[randomType])
} while self._testArr.contains(_randomTest)
}
}
All i want to do is to pick different objects. Lets say i have x2,y4,x6,z3,z8,y2 in _testArr. When i call randomTestPicker, it should not pick x2 or z8. Because they are already in array.
I have tried contains as you see. However it did not work for me. Is there any solution that i can use for this purpose? Or what is the best way to do this?
Edit: I tried self._testArr.contains{$0 === _randomTest} but not working neither.
You can't use contains that way since your class doesn't conform to the Equatable protocol.
Add :Equatable to your class definition and implement the == function which compares two of your objects:
class Test: Equatable {
private var _number: Int!
private var _type: String!
var number: Int {
return _number
}
var type: String {
return _type
}
init (number: Int, type: String) {
self._number = number
self._type = type
}
}
func ==(lhs: Test, rhs: Test) -> Bool {
return lhs.number == rhs.number && lhs.type == rhs.type
}
The other way this could have been done is to use the predicate form of contains. The predicate takes two objects and returns a Bool indicating if they match. In that case, you would write:
self._testArr.contains { $0.number == _randomTest.number && $0.type == _randomTest.type }
As you can see, in this case the closure is essentially the == function from above, so implementing the Equatable protocol is the cleaner way to do it.
The closure { $0 === _randomTest } doesn't work because that only tests if the objects are the same instance. In your case, you need to check if the two objects have the same properties, and you are not interested if they are same instance. The way you are creating the objects, you never would create an instance that is already in the array, so this check would always return false.

Remove array items from array

I'm trying to write an array extension that will allow me to remove items from an array if they exist in another array. I will need to use this for a range of custom types so was trying to build something generic.
This is what I have so far which returns the error "binary operator == cannot be applied to two t operands"
extension Array{
mutating func removeArrayItems<T>(itemsToRemove: [T]){
for item in itemsToRemove{
var removed = removeObject(item)
}
}
mutating func removeObject<T>(object: T) -> Bool {
for (idx, objectToCompare) in enumerate(self) {
if let to = objectToCompare as? T {
if object == to {
self.removeAtIndex(idx)
return true
}
}
}
return false
}
}
The equality operator, ==, only applies to objects that are Equatable. You need to qualify your type T as T:Equatable. As such:
mutating func removeObject<T:Equatable>(object: T) -> Bool {
// ...
}
Same with removeArrayItems
Note that if you are defining your own types, such as with class or struct, your types will also need to conform to Equatable. Like this:
class Foo : Equatable {
// ...
}
public func ==<T:Foo> (lhs:T, rhs:T) -> Bool {
// ...
}

Comparing protocol references

I have an array of protocols. now I want to remove an item from the array, by finding the index of the protocol with the array. however, when comparing the protocol object with the items in the array, the compiler warns with:
'Protocol' does not conform to AnyObject
protocol SomeProtocol {}
var list:[SomeProtocol] = []
func add(some:SomeProtocol) { list+=some }
func remove(some:SomeProtocol) {
var index = -1
for i in 0...list.count-1 { if [i] === some { index = i } }
if index >= 0 { list.removeAtIndex(index) }
}
If you derive only classes for the protocol, you can change protocol definition to:
protocol SomeProtocol: class {}
Then you will be able to use references with this protocol.
First of all, doing add is super easy, just include this function to make it work:
func +=(inout lhs: [SomeProtocol], rhs: SomeProtocol) {
lhs.append(rhs)
}
Doing remove is a lot trickier because SomeProtocol could apply equally to a class or struct, and only values with class types can be compared with ===.
We could use the == operator instead, but it only takes values that conform to the Equatable protocol, and Equatable can only be used as a generic constraint (so far), otherwise you could use something like protocol<SomeProtocol,Equatable> as your array element type.
If you know for sure that your SomeProtocol will only be applied to classes, consider refactoring your code to work with that class type instead:
protocol SomeProtocol {}
class SomeClass : SomeProtocol {}
var list:[SomeClass] = []
func add(some:SomeClass) {
list += some
}
func remove(some:SomeClass) {
list -= some
}
func +=(inout lhs: [SomeClass], rhs: SomeClass) {
lhs.append(rhs)
}
func -=(inout lhs: [SomeClass], rhs: SomeClass) {
for (i,v) in enumerate(lhs) {
if v === rhs {
lhs.removeAtIndex(i)
break
}
}
}
If you also happen to make SomeClass conform to Equatable the usual array functions will work automatically, and you won't even need to overload += and -=.
Otherwise, if you can't know whether your value will be a class or a struct, it might be better to think about what it means for values of SomeProtocol to be "equal" and require a comparison method:
protocol SomeProtocol {
func isEqualTo(some: SomeProtocol) -> Bool
}
func -=(inout lhs: [SomeProtocol], rhs: SomeProtocol) {
for (i,v) in enumerate(lhs) {
if v.isEqualTo(rhs) {
lhs.removeAtIndex(i)
break
}
}
}
// add functions work the same as above
Alternatively, you could use your original code and write a global comparison function:
func ===(lhs: SomeProtocol, rhs: SomeProtocol) -> Bool {
// return something based on the properties of SomeProtocol
}
I end up using isEqual(to: ) in my protocols to test for instance comparisons:
public protocol fooProtocol {
...
func isEqual(to: fooProtocol) -> Bool
}
public extension fooProtocol {
func isEqual(to: fooProtocol) -> Bool {
let ss = self as! NSObject
let tt = to as! NSObject
return ss === tt
}
}
Seems to work for me.

Resources