How to Cast Any to an Optional

Is it possible to cast Any to an Optional?

For Swift 2.0, you can use the following:

let x: Int? = 10
let y: Any = x
let z = Mirror(reflecting: y).descendant("Some") as? Int

Or as a function:

func castToOptional<T>(x: Any) -> T? {
return Mirror(reflecting: x).descendant("Some") as? T
}
let x: Int? = 10
let y: Any = x
let z: Int? = castToOptional(y)

Or you can do this if you don't like Reflection:

func castToOptional<T>(x: Any) -> T {
return x as! T
}
let x: Int? = 10
let y: Any = x
let z: Int? = castToOptional(y)

Why do we need to explicitly cast the optional to Any?

Every type can be implicitly promoted to an optional of that type. This means that when you cast T? to Any it is very hard to know whether it was originally T or originally T? (or even T?? or worse). Most confusing is that Any can be promoted to Any? and that Any? is of type Any, so telling the difference between Any, Any?, Any??, and Any??? (etc.) is very tricky, and sometimes impossible.

Any is a very tricky type in Swift and should almost never be used. Except for explicitly tricking the compiler (in some very fancy and fragile type-eraser), I don't know of any case where it really makes sense to have Any as a variable type, and definitely not in the form of [Any]. If you're created an [Any], you've gone down a bad path that isn't going to go well.

There are a very few cases where Any as a function parameter type makes sense (print() being the most famous), but they are extremely rare in app-level code. If you find yourself needing Any, you've probably done something wrong, and the compiler is going to fuss at you about it and often make you write extra as code to make sure you really mean the messy things you're saying.

Just to give some concrete versions of this, optionality tends to be lost when you enter Any. So consider this situation:

let number: Int = 3
let optionalNumber: Int? = 3
let nilNumber: Int? = nil

let anyNumber = number as Any
let anyOptional = optionalNumber as Any
let anyNil = nilNumber as Any

if anyNumber is Int { print("number is Int")} // yes
if anyOptional is Int { print("optional number is Int")} // yes
if anyNil is Int { print("nil is Int")} // no

if anyNil is Int? { print("nil is Int?")}
// -> Error: Cannot downcast from 'Any' to a more optional type 'Int?'

Rats.

We can't get our optional back the same way we put it in. We can promote it of course:

if (anyNil as Any?) is Int? { print("nil is Int?") }  // yes

But we can promote anything that way, since everything is implicitly an optional of itself:

if (anyNumber as Any?) is Int? { print("number is Int?")}  // also yes

So, um. Rats. We don't really know if it was originally optional or not. It's mess, and the compiler is warning you that it's going to be a mess if you go very far down this road. T->Any is a bit of magic. T->T? is also a bit of magic. Combine the two magics, and you had better know exactly what you're doing.

Cast the content of optional or streams

You can easily make this more fluent by relying on map()/flatMap() and cast methods that return functions instead.

For Optional, this is very easy since map() can act as a filter by returning null. So just define:

public static <U> Function<Object, U> filterAndCast(Class<? extends U> clazz) {
return t -> clazz.isInstance(t) ? clazz.cast(t) : null;
}

and use it as:

Optional<Number> number = Optional.of(42L);
System.out.println(number.map(filterAndCast(Integer.class)));
System.out.println(number.map(filterAndCast(Long.class)));

Output:

Optional.empty
Optional[42]

For streams you can apply more or less the same trick by relying on flatMap() with a function that returns an empty Stream:

public static <U> Function<Object, Stream<U>> streamFilterAndCast(Class<? extends U> clazz) {
return t -> clazz.isInstance(t) ? Stream.of(clazz.cast(t)) : Stream.empty();
// or alternatively
return t -> Stream.of(t).filter(clazz::isInstance).map(clazz::cast);
}

and use it as:

Stream.of(42L, "Hello world", 1024, 3.14)
.flatMap(streamFilterAndCast(Number.class))
.forEach(System.out::println);

Output:

42
1024
3.14

How do I cast an Any value with nil in it to a Any?

To simply check for nil content in the property value wrapped in an Any, you can, contrary to methods described in the other answers, actually work your way around casting/binding/checking to a concrete non-Any type by directly applying pattern matching to Optional<Any>.none or Optional<Any>.some(...).

Example setup (different members types: we don't want to annotate all these different types simply for reflection checking for nil content)

struct MyStruct {
let myString: String?
let myInt: Int?
let myDouble: Double?
// ...
init(_ myString: String?, _ myInt: Int?, _ myDouble: Double?) {
self.myString = myString
self.myInt = myInt
self.myDouble = myDouble
}
}

Simple logging: Extracting property names of nil valued properties

Pattern matching to Optional<Any>.none, if you simply want to log info on nil valued entities:

for case (let label as String, Optional<Any>.none) in 
Mirror(reflecting: MyStruct("foo", nil, 4.2)).children {
print("property \(label) is nil")
}
/* property myInt is nil */

Slightly more detailed logging: for nil as well as non-nil valued properties

Pattern matching to Optional<Any>.some(...), in case you want more detailed logging (the binded x value below corresponds to your guaranteed non-nil Any instance)

for property in Mirror(reflecting: MyStruct("foo", nil, 4.2)).children {
if let label = property.label {
if case Optional<Any>.some(let x) = property.value {
print("property \(label) is not nil (value: \(x))")
}
else {
print("property \(label) is nil")
}
}
}
/* property myString is not nil (value: foo)
property myInt is nil
property myDouble is not nil (value: 4.2) */

Or, the latter using a switch case instead:

for property in Mirror(reflecting: MyStruct("foo", nil, 4.2)).children {
switch(property) {
case (let label as String, Optional<Any>.some(let x)):
print("property \(label) is not nil (value: \(x))")
case (let label as String, _): print("property \(label) is nil")
default: ()
}
}
/* property myString is not nil (value: foo)
property myInt is nil
property myDouble is not nil (value: 4.2) */

How to upcast object contained in Java 8 Optional?

I would write a method like this:

@SuppressWarnings("unchecked")  // Safe. See below.
static <T> Optional<T> copyOf(Optional<? extends T> opt) {
return (Optional<T>) opt;
}

(If you don't like the name copyOf, see my comment about Guava's ImmutableList below)

This is very efficient in terms of runtime speed: the cast gets elided at compile time:

static <T> java.util.Optional<T> copyOf(java.util.Optional<? extends T>);
Code:
0: aload_0 # Read the parameter.
1: areturn # Return the parameter.

so the only cost is that of a method call; this is easily done away with by the JIT.

You can then invoke like:

Optional<A> a = copyOf(func2());

This is safe because Optional has the following property: it is guaranteed not to have any state changes caused by setter methods taking parameters dependent upon the type variable T. Phew. Quite a mouthful. I'll make it more concrete.

Because Optional

  1. has no setter methods (of any kind, but more generally none that take parameters of type T, SomeGenericType<T> etc)
  2. is final (so you can't subclass it to add a setter to violate the previous point)

there is nothing you can do to the value held by the Optional<T> (or lack thereof) that will make it not an instance of T (or lack thereof).

And because every instance of T is also an instance of its superclasses, there is nothing unsafe about:

SuperclassOfT s = optionalOfT.get();

As such, this method is type safe (it will fail if you've invoked it on a non-present optional; but that's not a type error).

You will find similar code in Guava's ImmutableList.copyOf (the inspiration for calling it "copyOf" above, even though it's not really a copy). There, there are setter methods (like add), but those methods immediately throw UnsupportedOperationExceptions, and thus do not affect the list's state.


Note that whilst immutable types have the necessary properties described above to make such a cast safe, the type does not necessarily need to be immutable to perform the cast safely.

For example, you could have an ErasableOptional<T> type, which has an erase() method on it which, when called, converted a "present" value into an "absent" value (i.e. get() no longer succeeds). It would be safe to cast such an instance to an ErasableOptional<SupertypeOfT> because the value is either a T or absent; you can't make it not an instance of SupertypeOfT or absent.



Related Topics



Leave a reply



Submit