More Generics
More Generics
The generics autocasting idea in
yesterday's entry has a disadvantage that I do not want to leave unmentioned. In the following example code
List<String> list1 = ArrayList()
Object list2 = ArrayList()
the constructor invocation 'ArrayList()' would have two different meanings. The first one creates an 'ArrayList<String>', the second one creates a 'ArrayList<Object>'. I don't think that this kind of ambiguity is acceptable. Therefore I need to forbid generics constructor invocations without type specification if the target type does not imply the types. Not too bad, the Object type is not needed that often anyway. Java allows it only for source-backward-compatibility reasons.
Beside the autocasting for constructors, my current concept for generics in Eek looks like this:
- The generic object is aware of the types it has been created with. Or more precisely, there is a Class instance for every instantiated class/type combination.
- The syntax for declaring a Map class as generic would be
class MyMap<Object K, Object V?>
end
Generic types use the same declaration syntax as variables. Thus they can be nullable (but do not have to) and the 'any' type is allowed. The syntax is simpler that Java's, but also less powerful. I am aware that re-using a declaration syntax (the one for variables) for a completely different purpose (giving a reference type a name) is not ideal, but it's better than having two different syntaxes.
Note that 'Object' is used in this example, but any other class or interface would be possible.
- The generic type aliases, 'K' and 'V' in the example, can be used almost like class names. Because 'K' is already nullable, it is allowed to create nullable 'K' references with the nullable modifier '?'. 'V' is already nullable, so it is forbidden to make 'V' instances nullable. Let's add some dummy methods to 'MyMap':
abstract class MyMap<Object K, Object V?>
abstract V get(K key)
abstract K? findKey(V value)
end
- Instantiation is done like in Java, just with possibility to make references nullable if the generic type is already nullable. Otherwise it must not be nullable. Here are a few legal instantiation examples (assuming the class is not abstract):
Object o1 = MyMap<Object,Object?>() // as generic as possible
Object o2 = MyMap<Object,Object>() // makes values non-nullable
Object o3 = MyMap<Object,any>() // Object? is compatible with any
Object o4 = MyMap<String,Object>() // maps strings on objects
Object o5 = MyMap<String,String?>() // maps strings on nullable strings
MyMap<Object,Object?> map1 = MyMap() // generic types implied by target
MyMap<String,String?> map2 = MyMap<String,String>() // legal cast, generic type compatible
MyMap<Object,Object> map3 = MyMap<String,String>() // legal cast, generic types compatible
MyMap<Object,Object?> map4() // compact constructor syntax is allowed
MyMap<String,any> map5() // compact constructor syntax is allowed
The following instantiations would be illegal:
Object o1 = MyMap<Object?,Object>() // Error: K must not be nullable
Object o2 = MyMap<any,Object>() // Error: K must not be any(which is nullable)
MyMap map1 = MyMap<Object, Object?>() // Error: variable map1 does not specify generic types
MyMap<String,String> map1 = MyMap<Object, Object>() // Error: generic types incompatible
MyMap<Object,Object> map2 = MyMap<Object,Object?>() // Error: generic type incompatible (nullable vs non-nullable)
Extending a generic class is possible. When extending the generic types may either be specified or may be kept generic:class StringMap extends MyMap<String, String>
end
class ExtendedMap<Object K, Object V> extends MyMap<K, V>
end
class StringKeyMap<Object T> extends MyMap<String, T>
It is not allowed to use the generic aliases of the super class(es) in the sub-class declaration or implementation.
written at 11:54. (0 Comments, Permalink)