Java compiler error: raw type with method returning Optional - javac

I'm trying to understand the Java compiler's thinking (I know, bad idea)...
Consider this program:
import java.util.Optional;
public class xx {
public static class Foo<T> {
public interface Bar<T> {
int getX();
}
public Optional<Bar<T>> getBar() {
return Optional.empty();
}
}
public static void main(String[] args) throws Exception {
Foo foo = new Foo(); // note raw type
foo.getBar().get().getX();
}
}
The java 1.8.0_112 compiler gives:
xx.java:15: error: cannot find symbol
foo.getBar().get().getX();
^
symbol: method getX()
location: class Object
1 error
The question is: why doesn't the compiler, given the raw type Foo for foo, realize that the return type of foo.getBar() is Optional<? extends Bar> instead of what it apparently thinks, which is Optional<?> ?
Note: I know how to change this program to make it compile, that's not the question.

Once you use raw types in conjunction with type inference, the following from JLS 18.5.2 will apply
If unchecked conversion was necessary for the method to be applicable during constraint set reduction in ยง18.5.1, then [...] the return type and thrown types of the invocation type of m are given by the erasure of the return type and thrown types of m's type.
From this follows, that the return type of foo.getBar() is indeed just Optional with all type arguments erased.
Solution: avoid raw types, always.

Related

How to use covariant in constructors?

Minimum reproducible code:
class Foo {}
class Bar extends Foo {}
class Baz {
final void Function(Foo) f;
Baz._(this.f);
Baz.one(void Function(Bar) func) : this._(func); // Compile error
}
I am getting this error:
The argument type 'void Function(Bar)' can't be assigned to the parameter type 'void Function(Foo)'
How can I use the covariant keyword to tell analyzer that a call to f will return Bar in my particular case?
Note: I can use this._(func as void Function(Foo)) to make it compile, but I'm looking for a better solution.

void Function(int) isn't a valid override of void Function(dynamic)

class Parent<T> {
void method(T t) {}
}
class Child extends Parent {
#override
void method(int i) {} // error: mentioned_below
void takesDynamic(dynamic d) {
takesType(d); // no error
}
void takesType(int i) {
takesDynamic(i); // no error
}
}
Error:
void Function(int) isn't a valid override of void Function(dynamic)
When I can easily pass int to dynamic and vice-versa in a method parameter, why do I see the error when I override method.
PS:
I am not looking for a solution which is to use extends Parent<int> and get it working, I want to know the reason why things are treated differently when I am overriding a method vs calling regular methods.
void Function(int x) normally isn't a valid override of void Function(dynamic x) because the int version is not substitutable for the dynamic version.
What are the allowed inputs to Parent<dynamic>.method? Anything.
What are the allowed inputs to Child.method? Just ints.
Such an override therefore could violate the contract of Parent<dynamic>'s interface. (For example, what if you had an instance of Child and passed it to something that expected Parent<dynamic>, which then invoked method('not an int') on it?)
(Note that this is not specific to method overrides. In general, a function that takes a narrower type cannot be used where a function that takes a wider type is expected, even if the narrower type derives from the wider type.)
Dart does allow you to use the covariant keyword to suppress the static type error and explicitly allow the override, but be aware that doing so isn't necessarily type-safe, and you would be responsible for ensuring that you don't get type errors at runtime.
Further reading: Covariance and contravariance (computer science) from Wikipedia

Dart TypeError: type is not a subtype of type Null

Consider the following code:
void printInt(int i) => print(i);
void printString(String s) => print(s);
void printSomething(Object o) {
final printer = {
int: printInt,
String: printString,
}[o.runtimeType];
print('Printer is $printer');
printer(o);
}
void main() => printSomething('Hello');
It prints the correct printString function and then crashes with the following exception:
TypeError: "Hello": type 'String' is not a subtype of type 'Null'
Why does that happen?
The error comes from the fact that your map has been given the type Map<Type, void Function(Null)> since that is the only type it can use based on the content of you list.
The problem is that Dart cannot give your map another type since anything else would not be valid from the perspective of the type system. Let's say the type was Map<Type, void Function(Object)>. Well, we are then allowed to send any object into a method from this map. But that is not allowed since your two methods in the map clearly are defined as accepting int and String and not Object.
We can either give it the type Map<Type, void Function(int)> since we have a method taking a String.
Also, Map<Type, void Function(dynamic)> has the same problem as Object since both methods are clearly defined to taking a precise type.
So Dart will instead use Null as the type of the parameter since the only thing we know is valid to give both methods are the null value.
And since you are then trying to give a String as parameter to a method with the signature of void Function(Null) you are getting an error from the type system.
If you want you code to run, you need to tell the type system to keep quite about what you are doing. To do that, you should use dynamic for the type of method you are receiving from the map:
void printInt(int i) => print(i);
void printString(String s) => print(s);
void printSomething(Object o) {
final dynamic printer = {
int: printInt,
String: printString,
}[o.runtimeType];
print('Printer is $printer');
printer(o);
}
void main() => printSomething('Hello');

Calling .filter() on a stream looses generic type

I have the following code. "Protectable" is an interface. My compiler gives the following error: "Incompatible types: Object cannot be converted to Collection"
When I remove the .filter line, everything works. Why does the compiler loose my type here?
Thanks,
Hannes
Collection<Protectable> requiredItems prefs.getConnectedProtectables(fuzDoc)
.stream()
.filter(protectable -> !protectable.itemVisibleForCurrentUser(fuzDoc))
.collect(Collectors.toList());
The variable prefs implements HasConnectedRights which is implemented as follows:
public interface HasConnectedRights {
public Collection<Protectable> getConnectedProtectables(FuzDocument doc);
}
The interface Protectable declares the method itemVisibleForCurrentUser like this:
default public boolean itemVisibleForCurrentUser(Docker<FuzDocument> doc) {
User user = UserCtrl.getCurrentUser(doc.getDoc());
return user == null || itemVisibleFor(user);
}

Syntax error when trying to determine if a variable is of a certain type

Pretty much as the title says: If you have a Type stored in a variable, there's no way to compare your actual object to this type variable, as far as I can tell. I can probably accomplish what I'm trying to do with mirrors, but I'd prefer not to if at all possible.
void example() {
Type myType = String;
String myExample = "Example";
//Syntax error here: The name 'myType' is not a type and cannot be used in an 'is' expression
if (myExample is myType) {
}
}
You can't generally test if a value is of a type using the Type object.
Type objects are reflected types, not real types. They represent the real type, but you can't use them in the code where you need a type: as type assertions, as generic type parameters or with the is/as operators. You must use the name of a type in those places, and not the name of a normal variable that happens to hold a Type object.
Clever stuff using mirrors might get there, but it's likely overkill for most cases (and I understand that you don't want it).
What you might be able to do instead, is to not pass around raw Type objects. You could instead make your own type abstraction, something like:
class MyType<T> {
const MyType();
Type get type => T;
bool isA(Object object) => object is T;
}
Then you can use that to represent types, not a Type object, and do something like:
void main(List<String> args) {
MyType myType = const MyType<String>();
String myExample = "Example";
if(myType.isA(myExample)) {
print('is');
} else {
print('is not');
}
}
That does require that your entire program uses your type objects to pass around types, but it also gives you a lot of control over those objects, so you can implement the functionality that you need.
I tried
library x;
void main(List<String> args) {
Type myType = String;
String myExample = "Example";
if(myExample.runtimeType == myType) {
print('is');
} else {
print('is not');
}
}
and it worked.
I have not much experience with such code in Dart though. Maybe that is not a fail-safe approach.
import 'package:reflection/reflection.dart';
void main() {
var childType = typeInfo(Child);
var baseType = typeInfo(Base);
if(childType.isA(baseType)) {
print("Child is Base");
}
if(baseType.isAssignableFrom(childType)) {
print("Base is assignable from Child");
}
}
class Base {
}
class Child extends Base {
}
Child is Base
Base is assignable for Child
P.S.
The "reflection" package incompatible with dart2js. It work only when used in Dart language.

Resources