Given there is something along this lines:
class Foo {}
class Bar extends Foo {}
class Baz extends Foo {}
I find myself writing this type of code:
if (foo is Bar) {
(foo as Bar).doSomething(); //Compiler warning of unnecessary cast.
} else (foo is Baz) {
(foo as Bar).doSomething(); //Compiler warning of unnecessary cast.
}
I don't know how to avoid this situation.
If I remove the check types of of is Bar or is Baz before the cast I may get a runtime error and if I don't cast, it means no access to the public stuff on that type.
Maybe I follow a flawed code design that I need to update because I believe I should avoid checking class type.
Is there any method in Dart that may help here?
(foo as? Bar)?.doSomething() (swift like)
Actually you can just do foo.doSomething(); instead of (foo as Bar).doSomething(). The compiler knows that if the condition if(foo is Bar) turns out to be true, the variable foo in that block of code is of type Bar.
Try:
if (foo is Bar) {
foo.doSomething(); // It's automatically inferred that type of `foo` is `Bar`
} else if (foo is Baz) {
foo.doSomething(); // It's automatically inferred that type of `foo` is `Baz`
}
Hope that helps!
With objects oriented languages this kind of if/else or switch statements based on class are solved using polymorphism. If doSomething() is in clase Foo the compiler knows that no matter run time type the method is defined on any child of Foo.
Then if you know that foo is Foo, Bar or Baz you actually should only use foo.doSomething(), no type check and no cast.
The only thing that you need to be aware is to validate that foo is Foo, make sure to use type and not var or dynamic:
void doSomething(dynamic foo){
if (foo is Foo) {
foo.doSomething(); //Ok with Foo Bar or baz
}
foo.doSomething(); //Possible runtime error since foo can be anything
}
Since dart is typed is a good practice to use types and not dynamic and you can write directly something like:
void doSomething(Foo foo){ //Making sure that you will work with a Foo
foo.doSomething(); //Ok with Foo Bar or baz with no check
}
Related
In the following code snippet, I defined two structs conforming to a common protocol. Then, I defined three overloads for a function: a concrete one, a generic one and an existential one:
protocol Foo { }
struct Bar: Foo { }
struct Baz: Foo { }
func process(_ data: Bar) { print("Bar") } // Concrete
func process<D: Foo>(_ data: D) { print("Generic") } // Generic
func process(_ data: any Foo) { print("Existential") } // Existential
process(Bar()) // prints "Bar"
process(Baz()) // prints "Existential"
Why does for the call to process(Baz()) the compiler chooses the existential overload while there is a more specific one, i.e. the generic one?
Also, I would have expected the compiler to enforce the programmer to write something like this for it to acknowledge that the instance is being wrapped in the existential type:
process(Baz() as Foo) // Explicit conversion to the existential type
Is this behavior expected, and is the only solution is to use the #disfavoredOverload attribute on the existential overload?
The compiler won't compile the below code, because it brokenly claims:
Protocol type Fooesque cannot conform to Fooesque because only concrete types can conform to protocols.
protocol Fooesque { var bar: Int { get set } }
class FooStruct: Fooesque { var bar = 42 }
struct Bar<F: Fooesque> {
typealias MyFoo = F
var foo: MyFoo
init(foo: MyFoo) { self.foo = foo }
}
struct Whee {
var someSpecificFoo: Fooesque
func process() { let bar = Bar(foo: someSpecificFoo) } // error line
}
var whee = Whee(someSpecificFoo: FooStruct())
Obviously this is a bug, because someSpecificFoo is already declared as a concrete instance that conforms to Fooesque (that's what var someSpecificFoo: Fooesque means).
If we try to fix by changing it to, var someSpecificFoo: some Fooesque, now the buggy compiler will say:
Property declares an opaque return type, but has no initializer expression from which to infer an underlying type
Now, not only will it fail to auto-synthesize an initializer expression like normal, but it will also fail even if we add one manually. Adding:
init<F: Fooesque>(foo: F) { self.foo = foo }
... does not fix anything—it just changes the buggy error message to:
Cannot assign value of type F to type some Fooesque
... even though F conforms to Fooesque! So broken!
Has anyone found a workaround for this mess?
So far the only fix I've found is to simply add #objc to the first line and import Foundation, in which case, it works perfectly.
I'm not a Swift compiler expert but I don't think that this is true.
Obviously this is a bug, because someSpecificFoo is already declared as a concrete instance that conforms to Fooesque (that's what var someSpecificFoo: Fooesque means).
It seems like the problem is exactly that. There is nothing concrete about that declaration. It could be anything that conforms to Fooesque. When you create a Bar with F as the protocol Fooesque you're saying Fooesque should conform to Fooesque which it cannot do. Only concrete types can do that.
If you change the line to var someSpecificFoo: FooStruct then it seems to work. But maybe that's not what you're going for.
Not sure what you're actually trying to do because these types are all so made up but maybe a more specific example would help?
Cheers!
It's not a buggy compiler; it's rather your misunderstanding of generics constraints.
Bar is a generic type that can be concretized with a specific type that conforms to Fooesque.
For example, you could have a Bar<FooStruct> type. This concrete type has the following init declaration:
init(foo: FooStruct)
Bar<FooStruct> does not accept just any type conforming to Fooesque - it only accepts FooStruct.
And it is explicitly NOT init(foo: Fooesque), but you expected it to behave as such, by conceptually doing the following:
let f: Fooesque = ...
let bar = Bar(foo: f)
What's the concrete type of Bar would you expect above?
The compiler infers F to be Fooesque based on the type of the argument, so it attempts to create a concrete type Bar<Fooesque>, which fails, because Fooesque - being a protocol - doesn't conform to other protocols, including itself.
But you'd probably say, no, I don't want the generic type to be a Fooesque; I want some type conforming to Fooesque.
Ok, which one? Let's say, it's FooStruct in the example above:
let f: Fooesque = ....
let bar = Bar<FooStruct>(foo: f) // ERROR
Again error, because Bar<FooStruct> expects a FooStruct as an argument - not any Fooesque.
Similarly, if you had AnotherFooStruct: Fooesque, then you would have a Bar<AnotherFooStruct> type, expecting AnotherFooStruct as an argument.
And thus - similarly - if you had any (generic) F type - aliased as MyFoo, in your example - then Bar<MyFoo> would only accept a MyFoo type - not Fooesque.
What that MyFoo type is, is determined at compile-time, either explicitly or implicitly based on the static type of the passed in parameter.
Let's say I have an array of type Foo with FooBar objects in it.
Bar <T> class is a generic class that inherits from Foo.
Then I have multiple FooBar classes, that inherits from Bar<T>, each with different generic type. (Bar<Int>, Bar<String>).
The problem is that I need to walk through the array of type Foo, check if na object is of type Bar<T>, cast it to Bar and set it's property.
Buty Swift won't allow me to cast to unknown type <T> and Bar<Any> doesn't match Bar<Int> or another...
I came up with two possible solutions - add new class to the hierarchy that inherits from Foo and is parent of Bar, but is not generic, and cast to this one or implement a protocol (which seems as much better solution).
What I'm really not sure about is if protocols are meant for this kind of stuff. In C-like lang, I would use an interface probably.
//Edit: Sample code
class Foo {
}
class Bar<T> : Foo {
var delegate : SomeDelegate
}
class FooBar: Bar<Int> {
}
class FooBar2: Bar<String> {
}
let arr : [Foo] = [ FooBar(), FooBar2(), Foo() ]
for item in arr {
// this is the problem - I need to match both String,Int or whathever the type is
if let b = item as? Bar<Any> {
b.delegate = self
}
}
Is there a name for a pattern where the type is inferred by the context of the result type?
Eg in this example what language could i use to document the foo method and explain that a type needs to be defined for the method to work?
protocol FooType {
init()
}
func foo<T: FooType>() -> T {
return T()
}
struct Bar: FooType {
init() {
print("bar")
}
}
let bar: Bar = foo()
// works returns instance of Bar
let fooType = foo()
// fails because foo doesn't know what type to use
You don't need to document this!
Everyone that writes code in Swift knows that to call a generic function, all its type parameters must be inferred and cannot be spoon-fed like this:
foo<Bar>()
People will see foo and say, "Oh I need the compiler to infer the type for this generic parameter." They will understand what this means.
What is the best way to unite to unrelated types under one protocol, e.g. to use that new type as an element type for an array?
e.g.:
Third-party-provided class Foo and class Bar are unrelated, however, I would like to create an array which contains elements of types Foo and Bar. Note that I can't change the implementation of Foo and Bar directly.
One approach might be:
protocol FooBar {}
extension Foo: FooBar {}
extension Bar: FooBar {}
However, this strikes me as odd and rather unappealing, since there are no implementations – are there other ways?
I will enumerate through that array and process the Elements knowing that it can be either Foo or Bar inside.
Not the answer directly to your question, but given that you only want to iterate through the array that contains Foos and Bars you could use enum like:
enum FooBar {
case WrappedFoo(Foo)
case WrappedBar(Bar)
}
This will at least save you from having to do as? with every element in order to get the exact type you are dealing with.
Example:
// Some setup
var arr: [FooBar] = []
let foo = Foo()
let bar = Bar()
arr.append(.WrappedFoo(foo))
arr.append(.WrappedFoo(foo))
arr.append(.WrappedBar(bar))
arr.append(.WrappedFoo(foo))
arr.append(.WrappedBar(bar))
arr.append(.WrappedBar(bar))
// Enumeration
arr.forEach {
switch $0 {
case .WrappedFoo(let foo):
// do something with `foo`
case .WrappedBar(let bar):
// do something with `bar`
}
}