Say I have the following class defined in java:
public class A
{
public class B
{
}
public B[] someFunc() {...}
}
And I am trying to access it in scala as follows:
val array: Array[A#B] = a.someFunc()
The compiler gives me the following warning:
*type mismatch; found : Array[a.B] required: Array[A#B] Note: a.B <: A#B, but class Array is invariant in type T. You may wish to investigate a wildcard type such as `_ <: A#B*
I am not sure of the correct syntax I should use to get over this error. I tried using the following but it will not compile:
val array: Array[T <: A#B] = a.someFunc()
But I have found away to get over the problem by passing it to a function:
def test[T <: A#B](array: Array[T]) = ...
test(a.someFunc())
which compiles fine.
How would I achieve the correct type assignment without having to define this test function?
Thanks
Des
Your B inner class is not marked static, which means, from the scala point of view, it is not a member of the companion object (i.e. a static member) A#B but it is a member of the instantiated object itself a.B.
So you should just declare your value this way:
val array: Array[a.B] = a.someFunc()
Or let type inference do it:
val array = a.someFunc() // typed as Array[a.B]
Edit: in case you do not have a reference to a lying around, usually you can just upcast a.B to A#B. Array is invariant, so that won't work, but there is a ruse: wrap it in something covariant.
val array: IndexedSeq[A#B] = a.someFunc()
You can use that one like an array, it doesn't actually convert anything (it is a WrappedArray), and you can call toArray on it if you really want to get an Array[A#B].
That is just a trick to avoid doing a.someFunc().asInstanceOf[Array[A#B]], but of course you could directly do that.
Related
When learning generics in Kotlin, I read in a book the following :
In general, a class or interface generic type may be prefixed with out if the class has functions that use it as a return type, or if the class has val properties of that type. You can’t, however, use out if the class has function parameters or var properties of that generic type.
I understand what the rule says, but i will be happy to understand (by examples) what may be without this rule (i.e there weren't constraint when using out when declaring a generic Class/Interface), and also why it isn't "dangerous" that the return type can be from type T and still class/Interface can contain out T.
Example where can't understand what is the problem that class property will behave as covariant:
class Pet{....}
class Dog:Pet{...}
class PetSomething <T : Pet>
{
T t;
public fun petDoSomething(T t)
{
.... // what can be the problem here?
}
}
class DogSomething
{
dogDoSomething()
{
d : Dog = Dog()
petDoSomething(d)
//what is the problem here???
}
}
In addition the book display the following code:
abstract class E<out T> (t:T) { val x = t }
and the code is being compiled although generic type is an input of constructor. Doesn't it break the rule?
You quoted: "You can’t, however, use out if the class has function parameters or var properties of that generic type."
A constructor is not a member function or property, so it is not subject to this rule. It is safe to use the type for a parameter at the site of the constructor, because the type is known when you are constructing it.
Consider these classes:
abstract class Pet
class Cat: Pet()
class Dog: Pet()
class PetOwner<out T: Pet>(val pet: T)
When you call the PetOwner constructor and pass in a Cat, the compiler knows you are constructing a PetOwner<out Cat> because it knows the value passed to the constructor satisfies the type of <out Cat>. It doesn't have to upcast Cat to Pet before the object is constructed. Then the constructed object can be safely upcast to PetOwner<Pet> because no T is ever going to be passed to the instance again. There is nothing unsafe that can happen, because no casting is done to the parameter.
Function parameters and var properties would be unsafe for an out type because the object is already constructed and might have been passed to some variable that has already upcast it to something else.
Imagine that the compiler let you define out T for a var property like this:
class PetOwner<out T: Pet>(var pet: T)
Then you could do this:
val catOwner: PetOwner<out Cat> = PetOwner(Cat())
val petOwner: PetOwner<out Pet> = catOwner
petOwner.pet = Dog()
val cat: Cat = catOwner.pet // ClassCastException!
The type safety rules prevent this scenario from being possible. But this isn't possible for a val constructor parameter. There is no way to pass the object to other variables and upcast its type in between passing the parameter to the constructor and having an instance that you can pass around.
The problem is this:
val x = DogSomething()
val y: PetSomething<Pet> = x // would be allowed by out
y.petDoSomething(Cat())
Note that petDoSomething on DogSomething only has to handle Dogs.
and the code is being compiled although generic type is an input of constructor. Doesn't it break the rule?
It doesn't, because the constructor isn't a member in the relevant sense; it couldn't be called on y above.
First lets clearfy what do we get by prefixing a type parameter with out keyword. consider the following class:
class MyList<out T: Number>{
private val list: MutableList<T> = mutableListOf()
operator fun get(index: Int) : T = list[index]
}
out keyword here makes MyList covariant in T, which essentially means you can do the following :
// note that type on left side of = is different than the one on right
val numberList: MyList<Number> = MyList<Int>()
if you remove the out keyword and try to compile again, you will get type mismatch error.
by prefixing the type parameter with out, you are basically declaring the type to be a producer of T's, in the example above MyList is a producer of Numbers.
which means no matter if you instantiate T as Int or Double or some other subtype of Number, you will always be able to get a Number from MyList (Because every subtype of Number is a Number). which also allows you to to do the following:
fun process(list: MyList<Number>) { // do something with every number }
fun main(){
val ints = MyList<Int>()
val doubles = MyList<Double>()
process(ints) // Int is a Number, go ahead and process them as Numbers
process(doubles) // Double is also a Number, no prob here
}
// if you remove out, you can only pass MyList<Number> to process
Now lets answer With out keyword why T should only be in return position? and what can happen without this constraint?, that is if MyList had a function taking T as parameter.
fun add(value: T) { list.add(T) } // MyList has this function
fun main() {
val numbers = getMyList() // numbers can be MyList<Int>, MyList<Double> or something else
numbers.add(someInt) // cant store Int, what if its MyList<Double> ( Int != Double)
numbers.add(someDouble) // cant do this, what if its MyList<Int>
}
// We dont know what type of MyList we going to get
fun getMyList(): MyList<Number>(){
return if(feelingGood) { MyList<Int> () }
else if(feelingOk> { MyList<Double> () }
else { MyList<SomeOtherSubType>() }
}
that is why constraint is required, its basically there to guarantee type safety.
as for abstract class E<out T> (t:T) { val x = t } being compiled, Kotlin In Action has following to say
Note that constructor parameters are in neither the in nor the out
position. Even if a type parameter is declared as out, you can still
use it in a constructor parameter. The variance protects the class
instance from misuse if you’re working with it as an instance of a
more generic type: you just can’t call the potentially dangerous
methods. The constructor isn’t a method that can be called later
(after an instance creation), and therefore it can’t be potentially
dangerous.
Consider the following example:
public class Learn {
public static <T> T test (T a, T b) {
System.out.println(a.getClass().getSimpleName());
System.out.println(b.getClass().getSimpleName());
b = a;
return a;
}
public static void main (String[] args) {
test("", new ArrayList<Integer>());
}
}
In the main method, I am calling test with a String and an ArrayList <Integer> object. Both are different things and assigning an ArrayList to String (generally) gives a compile error.
String aString = new ArrayList <Integer> (); // won't compile
But I am doing exactly that in the 3rd line of test and the program compiles and runs fine. First I thought that the type parameter T is replaced by a type that's compatible with both String and ArrayList (like Serializable). But the two println statements inside test print "String" and "ArrayList" as types of a and b respectively. My question is, if ais String and b is ArrayList at runtime, how can we assign a to b.
For a generic method, the Java compiler will infer the most specific common type for both parameters a and b.
The inference algorithm determines the types of the arguments and, if available, the type that the result is being assigned, or returned. Finally, the inference algorithm tries to find the most specific type that works with all of the arguments.
You aren't assigning the result of the call to test to anything, so there is no target to influence the inference.
In this case, even String and ArrayList<Integer> have a common supertype, Serializable, so T is inferred as Serializable, and you can always assign one variable of the same type to another. For other examples, you may even find Object as the common supertype.
But just because you have variables of type T that are inferred as Serializable, the objects themselves are still a String and an ArrayList, so getting their classes and printing their names still prints String and ArrayList. You're not printing the type of the variables; you're printing the type of the objects.
test("", new ArrayList<Integer>());
This is equivalent to the following:
Learn.test("", new ArrayList<Integer>());
Which is also equivalent to the following:
Learn.<Serializable>test("", new ArrayList<Integer>());
This will not compile if you explicitly specify a generic type other than Serializable (or Object), such as String:
Learn.<String>test("", new ArrayList<Integer>()); // DOES NOT COMPILE
So essentially both parameters are treated as Serializable in your case.
Consider this piece of code:
public class Main {
public static void main(String[] args) {
Cat<Integer> cat = new Cat();
Integer i= cat.meow();
cat.var = 6;
}
}
public class Cat<E> {
public E var;
public E meow() {
return null;
}
}
As per my understanding since I've not specified the type parameter on LHS, it would be taken as Object. And Cat should become Cat<Object> because for the variable declaration to make any sense T must translate to a Class/Interface reference. Is this a correct understanding of how it works? How is type parameter T handled in case of raw types?
I've discussed this on chat and got following explanation which is way over my head:
Generics works because types are erased. Consider T an erasure of
#0-capture-of-Object. When T isn't specified (rawtyped), it is #0-capture-of-(nothing)
What does #0-capture-of-(nothing) mean?
Side note: since generic types are implemented as compile time transformations it would be easier to understand them if one could see the final translated code. Does anyone know a way to see the translated code (not byte code) for a class?
No,
raw types are not as if they are paramterized with Object, nor are they like wildcard types (<?>).
For raw types, generics are turned off.
This code is compiles (with warnings):
Cat c1 = new Cat<String>();
Cat<Integer> c2 = c1;
This code does not:
Cat<? extends Object> c1 = new Cat<String>(); // btw: this is the same as Cat<?>
Cat<Integer> c2 = c1; // error
neither does this:
Cat<Object> c1 = new Cat();
Cat<Integer> c2 = c1; // error
As for the type of var:
the type of the field after compilation is whatever the upper-bound of the parameter is (Object if none is specified). But what does the compiler do if we access var?
Cat<String> c1 = ...
String c1Var = c1.var;
This code compiles without error, but what the compiler will actually compile is this:
Cat c1 = ...
String c1Var = (String) c1.var;
As you can see, var is always treated as a field of type Object, but with generics, the compiler automatically inserts type-safe casts. That's all. If you use raw types, you have to do it yourself. Either way, when you put a Cat that stores an integer in a Cat<String> variable, you will only get a runtime exception if you try to read var.
A quiz
Now look at the declaration of Collections.max. Why do you think the parameter is defined as T extends Object & Comparable<? super T>?
Answer encoded in rot13:
Fb gung nsgre pbzcvyngvba gur erghea glcr vf Bowrpg, abg Pbzcnenoyr. Guvf vf arrqrq sbe onpxjneqf pbzcngvovyvgl (gur zrgubq vgfrys vf byqre guna trarevpf).
Edit:
Here is another good example that I just stumbled upon:
class Foo<T> {
public <V> V bar(V v) { return v; }
}
//compiles
Foo<Object> foo = new Foo<Object>();
Integer i = foo.bar(1);
//compiles
Foo<?> foo = new Foo<String>();
Integer i = foo.bar(1);
// fails
Foo foo = new Foo();
Integer i = foo.bar(1); // error: Object cannot be converted to Integer
Using no parameters disables generics entirely.
This code is valid:
Cat c = new Cat<Integer>();
c is now of the Raw Type Cat.
This is not valid:
Cat<Object> c = new Cat<Integer>(); // Compiler error
So, it's not exactly the same. Though you can, after the first line, do things like:
c.var = 5;
System.out.println(c.var);
c.var = 1;
System.out.println(c.var);
c.var = "test";
System.out.println(c.var);
Outputs:
5
1
test
#Cephalopod has provided the correct answer, however I'd like to expand on that with some of my own explanation.
for the variable declaration to make any sense T must translate to a Class/Interface reference.
That is correct. Generics are a compile time transformation. Runtime system has no notion of abstract types. So before the class is loaded into memory the abstract type T must be replaced by an actual type reference.
Run the following code:
System.out.println(Cat.class.getMethod("meow").getReturnType());
System.out.println(Cat.class.getField("var").getType());
The output is:
class java.lang.Object
class java.lang.Object
The formal type parameter E has been replaced with Object.
Cat should become Cat<Object>
Wrong. Cat will stay Cat. Why? Look at the decompiled class file for Main:
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
Integer i = (Integer)cat.meow();
cat.var = Integer.valueOf(6);
}
}
The purpose of specifying formal type parameter with <> is to enable compiler to generate explicit casts.
When you say new Cat() it doesn't have to turn into anything, the compiler simply won't generate a cast and the method call would look like:
Integer i = cat.meow(); // No cast at all
Are generic type parameters converted to Object for raw types?
To clarify what is being asked here, the above questions means: Is E replaced with java.lang.Object if I don't specify anything when instantiating Cat.
Actually E would be replaced with java.lang.Object even if you specified <Integer> when instantiating Cat. The replacement/transformation is done at compile time while the instantiation is at runtime. How you use the type isn't going to change its class definition.
Generic types defined in objects like the
Cat c = new Cat<Integer>();
are only intended to provide the compiler with the chance to check that the types will match at runtime.
Generic types assigned in class definitions are retained in the compiled class.
public class Cat<T extends Number>{}
Or
public class Intcat extends Cat<Integer>{}
The runtime knows that the generic argument is bound by Number in the first case and is Integer in the first case.
I have no links to back this up, but I'd rather assume that c becomes raw type Cat, not Cat<Object>.
Raw types don't handle parameter T, which is why they are prone to errors.
javadoc says: A raw type is the name of a generic class or interface without any type arguments.
That chat log seems to mean exactly that, but in a confusing manner.
I actually Do not know How it is actually implemented in the bytecode But from my understanding Cat c = new Cat<Integer>(); Stores the new instance of Cat created by new Cat<Integer>() in the variable c. Now if you query c to know what is the type of var it will reply Integer and not Object because the instance that was created has a type of Integer.
Now If you execute c.var = "Text"; and query c to know what is the type of var. It would reply String. This does not means that by default it is converting <T> to Object. It means that c does not know what is the type of var.
I feel that is why the <?> wild card is used. Cat<?> c = new Cat<Integer>(); it would always convert <T> to Object. That is the reason why it is always advised not the use raw types for generics.
I think Cat c is a RAW type and could be considered as a "wildcard type" like Cat<?>. Since Cat<?> is the supertype of each type of Cat including Cat<Integer>, Cat c may take a new Cat<Integer> object.
This is also mentioned here: Interoperating with Legacy Code
"Most people's first instinct is that Collection really means Collection. However, as we saw earlier, it isn't safe to pass a Collection in a place where a Collection is required. It's more accurate to say that the type Collection denotes a collection of some unknown type, just like Collection."
...
"So raw types are very much like wildcard types, but they are not typechecked as stringently. This is a deliberate design decision, to allow generics to interoperate with pre-existing legacy code."
Note: purely out of curiosity and not for any actual use case.
I'm wondering if there is a way to declare the Class Class object with valid type parameters:
Class cc1 = Class.class; //raw type
Class<Class> cc2 = Class.class; //now parameter is raw type
Class<Class<?>> cc3 = Class.class; //compile error: inconvertible types
If Class and Class<?> are interchangeable, why are Class<Class> and Class<Class<?>> not?
EDIT: the question can be generalized to an issue of nested raw type parameters. For example:
ArrayList<ArrayList<?>> lst = new ArrayList<ArrayList>(); //same compile error
EDIT2: I should rephrase the question a little: I know that
Class<?> c = Class.class;
is valid but I'm wondering why Class<Class> is not the same as Class<Class<?>>
Generics have some pretty serious limitations. In this case you can't assign a type to the inner type of Class<Class> because you're actually referencing the raw type, not an implementation of the raw type. It will give you a warning, but you have no way to fix that warning.
Class<Class<?>> by itself isn't an inconvertible type, you just can't assign a class directly to it because it doesn't have the type Class<Class<T>>, it has the type Class<T>.
Think of it another way; try List<List<String>>. To create that, you need to create a List that takes a List of Strings. This works because lists can contain lists.
A Class is more like a primitive than a data object, so I don't think it'd be possible to create a Class that is of type Class of something else.
Edit: your extra question about ArrayList<ArrayList<?>> is a more obvious example of the inconvertible type issue you're having with Class<Class<?>>.
The rule here is that the generic type in the left side must match the generic type in the right side.
Class<?> means a class of any type.
Class<?> c = Class.class;
Works because Class of any type can be Class<Class>.
Class<Class<?>> cc3 = Class.class;
Do not work because Class.class type is Class<Class> which is not of type Class<Class<?>>
ArrayList<ArrayList<Integer>> lst = new ArrayList<ArrayList<Integer>>();
ArrayList<ArrayList<?>> lst = new ArrayList<ArrayList<?>>();
Works because the two expressions match.
ArrayList<ArrayList<?>> lst = new ArrayList<ArrayList>();
Don't match.
It's kind of difficult to see exactly what you're asking (or what you're trying to do).. but you can parametrize without raw types.
Class<? extends Object> cc4 = Class.class; // no raw types
Class<?> cc5 = Class.class; // also an option
As far as your last example is concerned, it makes no sense because it appears you want to make an array list of array lists that hold ?, but your declaration isn't declaring an array list of array lists that hold ?.
Properly written (but still not proper Java) would be:
ArrayList<ArrayList<?>> lst = new ArrayList<ArrayList<Integer>>(); // Type mismatch
Which is expected. It doesn't work for the same reason something like the following doesn't work:
Object o = new Object();
Integer i = new Integer(3);
o = i;
i.floatValue();
o.floatValue(); // can't access that method, we need to specifically cast it to Integer
Java types aren't proactively inferred (even in an inheritance chain).
If you want to keep the wildcard in there, you're welcome to, though:
ArrayList<ArrayList<?>> lst = new ArrayList<ArrayList<?>>(); // works!
I have a question about Generics in Java, namely using wildcards. I have an example class GenClass like this:
public class GenClass<E> {
private E var;
public void setVar(E x) {
var = x;
}
public E getVar() {
return var;
}
}
I have another simple class:
public class ExampleClass {
}
I have written the following test class:
public class TestGenClass {
public static void main(String[] str) {
ExampleClass ec = new ExampleClass();
GenClass<ExampleClass> c = new GenClass<ExampleClass>();
c.setVar(ec);
System.out.println(c.getVar()); // OUTPUT: ExampleClass#addbf1
}
}
Now, if I use a wildcard and write in the test class this:
GenClass<?> c = new GenClass<ExampleClass>();
on the place of:
GenClass<ExampleClass> c = new GenClass<ExampleClass>();
the compiler has no problem with this new statement, however, it complains about
c.setVar(ec);
It says that "the method (setVar()) is not applicable for the arguments (ExampleClass)". Why do I get this message?
I thought that the way I have used the wildcard, makes the reference variable c be of type GenClass, which would accept as parameter any class - on the place of E I would have any class. This is just the declaration of the variable. Then I initialize it with
new GenClass<ExampleClass>()
which means that I create an object of type GenClass, which has as parameter a class of type ExampleClass. So, I think that now E in GenClass will be ExampleClass, and I would be able to use the method setVar(), giving it as argument something of type ExampleClass.
This was my assumption and understanding, but it seems that Java does not like it, and I am not right.
Any comment is appreciated, thank you.
This exact situation is covered in the Java Generics Tutorial.
Notice that [with the wildcard], we can still read elements from [the generic Collection] and give them type Object. This is always safe, since whatever the actual type of the collection, it does contain objects. It isn't safe to add arbitrary objects to it however:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
Since we don't know what the element type of c stands for, we cannot add objects to it. The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don't know what type that is, we cannot pass anything in. The sole exception is null, which is a member of every type.
(emphasis mine)
mmyers has the correct answer, but I just wanted to comment on this part of your question (which sounds like your rationale for wanting to use the wildcard):
I thought that the way I have used the wildcard, makes the reference variable c be of type GenClass, which would accept as parameter any class - on the place of E I would have any class. This is just the declaration of the variable. Then I initialize it with
If you really want to accomplish this, you could do something like without compilation errors:
GenClass<Object> gc = new GenClass<Object>();
gc.setVar(new ExampleClass());
But then again, if you want to declare an instance of GenClass that can contain any type, I'm not sure why you'd want to use generics at all - you could just use the raw class:
GenClass raw = new GenClass();
raw.setVar(new ExampleClass());
raw.setVar("this runs ok");