Java generics casting to <?> - java

I have the following class
public class DBField<T>
{
protected String fieldName;
protected FieldConverter c;
protected T value;
protected DataObject dataObject;
public T getValue()
{
return value;
}
public void setValue(T value)
{
this.value = value;
}
public DBField(DataObject dataObject, String fieldName, FieldConverter c)
{
this.fieldName = fieldName;
this.c = c;
this.dataObject = dataObject;
}
}
T is supposed to be Boolean, Float, String etc.
protected void ValuesToFields(List<Object> values, List<DBField<?>> fields) throws Exception
{
if (values.size() != fields.size())
throw new Exception("Length does not match.");
for (int i = 0; i < values.size(); i++)
{
Class valueClass = values.get(i).getClass();
Class fieldClass = fields.get(i).getValue().getClass();
if (valueClass.equals(fieldClass))
{
fields.get(i).setValue(values.get(i));
}
else
throw new Exception("type mismatch");
}
}
Object is also supposed to contain Boolean, Float, String etc.
The problem with this code is
fields.get(i).setValue(values.get(i));
The syntax checker tells me I need to cast values.get(i) (to ? i suspect). How do I do this? I already tried valueClass.cast(values.get(i)) but no luck.

In order for your code to be safe, for each i, the i'th element of values must be an instance of the type parameter of the DBField that is the i'th element of fields. Your code does not guarantee that this holds, and in fact there is no way to declare them in Java to ensure that this relationship between corresponding elements is true. And due to type erasure, you can't even check at runtime that the elements are right, because given a field, you don't know its type parameter. So there must be some unchecked casts, and we must take on faith that the arguments are correct.
The simplest thing to do would be to cast each field to DBField<Object>:
((DBField<Object>)fields.get(i)).setValue(values.get(i));
This is kind of saying "trust us, we know that this field can take any Object", and thus it can take a value of any type. It is kind of lying, because we know there are supposed to be fields whose type parameter is not Object, but since we must make some kind of unchecked cast anyway, this "unsafe cast" is no worse than the other solutions.
Alternately, if you don't want to do this arguably dubious cast, a more "legitimate" way would be to write a private helper method -- a "wrapper helper" -- which explicitly names the type parameter of the field, allowing us to simply cast to the value to this type:
private <T> static void ValueToField(Object value, DBField<T> field) {
field.setValue((T)value);
}
//...
ValueToField(values.get(i), fields.get(i));
Note that the cast here is also an unchecked cast. The disadvantage of this method is that it requires the overhead of writing an extra method.
P.S. Your checks with valueClass and fieldClass are not very good. First of all, if the value of a field is currently null, it will cause a null pointer exception. Also, the value of a DBField<T> is any instance of T, whose actual class may be a subclass of T; so if you use this to check, it might lead to bad results. It's probably best if DBField contains the class object of the class of T, so it can be used to check. Also, you shouldn't compare equality with the value's actual class, since a subclass of T would also work, so you should check fieldClass.isInstance(values.get(i)) instead.

if(values.get(i) instanceof valueClass) ?

You have a List<Object>.
You should have a List<DBField<Object>> to match it, or change your first list to List<?>

Related

Type checking with generic Suppliers and lambdas

I have two generic methods, which are designed to force the caller to provide parameters that match type wise:
private <T> void compareValues(Supplier<T> supplier, T value) {
System.out.println(supplier.get() == value);
}
private <T> void setValue(Consumer<T> consumer, T value) {
consumer.accept(value);
}
However, when calling them, the compiler reasons differently on what is allowed to pass as parameters:
compareValues(this::getString, "Foo"); // Valid, as expected
compareValues(this::getInt, "Foo"); // Valid, but compiler should raise error
compareValues(this::getString, 1); // Valid, but compiler should raise error
setValue(this::setString, "Foo"); // Valid, as expected
setValue(this::setInt, "Foo"); // Type mismatch, as expected
setValue(this::setString, 1); // Type mismatch, as expected
private String getString() {
return "Foo";
}
private int getInt() {
return 1;
}
private void setString(String string) {
}
private void setInt(int integer) {
}
How come? Is the compiler just too clumsy to properly reason about types here, or is this a feature of the type system? If so, what are the rules that lead to this behavior? Also, how would I create a "type safe" version of compareValues without adding artificial parameters, if at all possible?
Please note, that the provided methods merely contain a dummy implementation and do not reflect the code in my actual code base. The focus here are solely the method calls.
Others have mentioned why this is happening, so here's a solution to get around the problem.
If you create a generic class, separating the passing of the supplier from the passing of the argument, you do not give the compiler the opportunity to choose an intersection type:
public class Comparer<T>
{
private final Supplier<T> supplier;
Comparer(final Supplier<T> supplier)
{
this.supplier = supplier;
}
void compare(T value)
{
System.out.println(supplier.get() == value);
}
}
new Comparer<>(this::getString).compare("Foo"); // Valid, as expected
new Comparer<>(this::getInt).compare("Foo"); // Invalid, compiler error
new Comparer<>(this::getString).compare(1); // Invalid, compiler error
By separating out this behaviour, you also allow Comparer to do potentially useful things like caching the result of Supplier.get().
You can tell that the compiler choose an intersection type, by using
javac -XDverboseResolution=deferred-inference
output in one of the cases is:
instantiated signature: (Supplier<INT#1>,INT#1)void
target-type: <none>
where T is a type-variable:
T extends Object declared in method <T>compareValues(Supplier<T>,T)
where INT#1,INT#2 are intersection types:
INT#1 extends Object,Serializable,Comparable<? extends INT#2>
INT#2 extends Object,Serializable,Comparable<?>
Well here T can be anything. It is a synonym of a type but can be basically any type.
So when you have a compareValues(Supplier<T> supplier, T value) it means a supplier that can give me any type and value that can be of any type. So it doesn't give a compile error and it even works. In your method you can do:
private <T> void compareValues(Supplier<T> supplier, T value) {
value=supplier.get(); //It is still valid even if you give different types
System.out.println((supplier.get() == value) +" - "+ value);
}
As for the other method it is different because you say "Give me a consumer that accepts any type" but you give him a consumer that accepts just String.
So here
private void setString(String s) {
}
won't work but
private <T> void setString(T s) {
}
will work just fine.
It's like if you have a variable of type Object you can assign String to it but not the other way around in a more bizarre situation. A String supplier is a <T> supplier but a String consumer is not a <T> consumer.
See these two methods:
private <T> void setString(T a) {
T var=a;
T var2="Asdf"; //This doesn't compile! cannot convert String to T
}
private <String> void setString2(String a) {
String var=a;
String var2="asd";
}
You want consumer of type T which the first method is. But instead you try to give a consumer of type String which cannot work because it consumes just Strings and you want a method that can consume everything

How do parameterized methods resolve <T> if it's not an input parameter?

How are references to << T >> handled by the compiler in the following code, since the method takes no parameters that would allow inference of T? Are any restrictions being placed on what type of object can be placed into the list? Is a cast even taking place on the line where I add the String to the list? My first thought is that without anything to infer T from, T becomes an Object type. Thanks in advance.
public class App {
private <T> void parameterizedMethod()
{
List<T> list = new ArrayList<>();
for(int i = 0; i < 10; i++)
{
list.add((T)new String()); //is a cast actually occurring here?
}
}
public App()
{
parameterizedMethod();
}
public static void main(String[] args) {
new App();
}
}
This is initially determined by 18.1.3:
When inference begins, a bound set is typically generated from a list of type parameter declarations P1, ..., Pp and associated inference variables α1, ..., αp. Such a bound set is constructed as follows. For each l (1 ≤ l ≤ p):
If Pl has no TypeBound, the bound αl <: Object appears in the set.
Otherwise, for each type T delimited by & in the TypeBound, the bound αl <: T[P1:=α1, ..., Pp:=αp] appears in the set; [...].
At the end of inference, the bound set gets "resolved" to the inferred type. Without any additional context, the bound set will only consist of the initial bounds based on the declaration of the type parameter.
A bound with a form like αl <: Object means αl (an inference variable) is Object or a subtype of Object. This bound is resolved to Object.
So in your case, yes, Object is inferred.
If we declared a type bound:
private <T extends SomeType> void parameterizedMethod()
then SomeType will be inferred.
No cast actually happens in this case (erasure). That's why it's "unchecked". A cast only happens when the object is exposed due to e.g.:
<T> T parameterizedMethodWithAResult()
{
return (T) new String();
}
// the cast happens out here
Integer i = parameterizedMethodWithAResult();
// parameterizedMethodWithAResult returns Object actually,
// and we are implicitly doing this:
Integer i = (Integer) parameterizedMethodWithAResult();
Are any restrictions being placed on what type of object can be placed into the list?
Semantically (compile-time), yes. And note that the restriction is determined outside the method. Inside the method, we don't know what that restriction actually is. So we should not be putting String in a List<T>. We don't know what T is.
Practically (run-time), no. It's just a List and there's no checked cast. parameterizedMethod won't cause an exception...but that only holds for this kind of isolated example. This kind of code may very well lead to issues.
Inside the method body, Java provides us no way to get any information about the substitution for T, so how can we do anything useful with T?
Sometimes, T is not really important to the method body; it's just more convenient for the caller
public static List<T> emptyList(){...}
List<String> emptyStringList = emptyList();
But if T is important to method body, there must be an out-of-band protocol, not enforceable by the compiler, that both the caller and the callee must obey. For example
class Conf
<T> T get(String key)
//
<conf>
<param name="size" type="int" ...
//
String name = conf.get("name");
Integer size = conf.get("size");
The API uses <T> here just so that the caller doesn't need to do an explicit cast. It is the caller's responsibility to ensure that the correct T is supplied.
In your example, the callee assumes that T is a supertype of String; the caller must uphold that assumption. It would be nice if such constraint can be expressed to the compiler as
<T super String> void parameterizedMethod()
{
List<T> list
...
list.add( new String() ); // obviously correct; no cast is needed
}
//
this.<Integer>parameterizedMethod(); // compile error
unfortunately, java does not support <T super Foo> ... :) So you need to javadoc the constraint instead
/** T must be a supertype of String! **/
<T> void parameterizedMethod()
I have an actual API example just like that.
List<T> list = new ArrayList<>();
for(int i = 0; i < 10; i++)
{
list.add((T)new String()); //is a cast actually occurring here?
}
No, no cast is actually occurring there. If you did anything with list that forced it to be a List<T> -- such as returning it -- then that may cause ClassCastExceptions at the point where the compiler inserted the real cast.

is it possible assign different return type for method

My idea is that there is a validator interface, which has method getRealValue(). The return value depends on field, it could be String, Integer or Long values.
My chances are:
I can do assign return type as Object and use casting every time after I called this method. (RuntimeError if wrong casting happened).
I can use generic an pass return type to validator when instantiate it (and I still have to use casting but inside method getRealValue and only once). Still RuntimeError if I will forget to pass return type or pass wrong type.
If there is a way I can store return type inside validator and use it?
For your 1st point, there is no way around getting a ClassCastException at runtime in case of an inappropriate cast.
In your second case you won't need to cast, see example here:
public interface Foo<T> {
public T getValue();
}
... then somewhere else:
public class Blah<T> implements Foo<T> {
#Override
public T getValue() {
// TODO write the code
// note that because of type erasure you won't know what type T is here
return null;
}
}
... then, somewhere else:
Blah blah1 = new Blah<String>();
String s = blah1.getValue();
Blah blah2 = new Blah<Long>();
// etc.
Finally, here's some literature for you:
Generics in Java
Inheritance in Java (has a section on casting)

How to name class name according to Java Generics type return?

< T > T foo(P p) {
...
}
I'll get different types of return from foo according to the parameter I inserted, which means T changes according to p.
Then I try to call this function and use its return result.
Class x = foo(p);
What should I write in substitute of Class here?
Suppose parameter is a enum type.
enum P {
XX,YY,ZZ
}
then the return type T is Xx, Yy, Zz respectively according to parameter.
Let me give the exact sample here.
public <T> List<T> getProperty(Property property) {
switch(property) {
case NAME: List<Name> names = new ArrayList<Name>();
names.add(this.name); return (List<T>) names;
case PHONE: return (List<T>) this.phones;
case EMAIL: return (List<T>) this.emails;
case ADDRESS: return (List<T>) this.addresses;
case NOTE: List<Note> notes = new ArrayList<Note>();
notes.add(this.note); return (List<T>) this.note;
default: return null;
}
}
public enum Property {
NAME, PHONE, EMAIL, ADDRESS, NOTE
}
public List<Entry> search(Property property, String s) {
if(this.isEmpty()) {
return null;
}
List<Entry> result = new ArrayList<Entry>();
for(Entry e : entries) {
if(e.getProperty(property) != null) {
for( **Object** p : e.getProperty(property)) { //What should I write instead of Object
if(p != null) {
if(p.containString(s)) { //there'll be errors if use Object. Need to know p's class.
result.add(e);
}
}
}
}
}
return this.nonDuplicatedResult(result);
}
I'm really not sure what you're asking. You haven't explained your use cases at all and haven't given us much code to look at. It's difficult to provide useful feedback from within the fog of obfuscation.
Generally speaking, if you want a method that returns a different object depending on the value of a supplied argument, then what you're probably talking about is a static factory method which can return any object that is a subtype of the method's return type. It is convenient to make such objects a part of an interface-based type system (eg. the static factories for the EnumSet class).
The use of an interface-based type system is actually necessary if you wish to return an enum, because enums cannot be part of a class hierarchy, but they can implement an interface that forms an interface-based type system.
Suppose parameter is a enum type.
enum P {
XX,YY,ZZ
}
then the return type T is Xx, Yy, Zz respectively according to parameter.
No it isn't. The return type is P. You're over-thinking this. The 'enum' case isn't a job for Generics at all.
Assuming you have an finite number of return types, you could just check through each one using instanceof to see if the returned value is of a certain type. So in this case Object would substitute class, then you could later cast it.
What is happening is basically with that method definition, what you are saying to the compiler is that your method will return whatever the parameter assignment declares itself to be. You can do that, but how your method is implemented will boil down to a compiler warning, as you can't actually ensure that you are generating the appropriate type - since you don't know it, it is never passed to the method, and erased at compile time.
More typically what you would do is:
<T extends P> T foo(T p) {
...
}
Now you get an object of the right type as a parameter, so you have some idea of what to return. That concept has little utility with enums, though.
Here is a real world example of where you could use the method definition you posed in your question:
public static <T> T generateProxy(Object realObject, Class<?>... interfaces) {
return (T) Proxy.newProxyInstance(realObject.getClass().getClassLoader(), interfaces, new SimpleInvocationHandler(realObject));
}
Of course such a method generates a compiler warning. Now, what you are saying here is that the caller will define a variable, and will be sure to pass in at least one interface of the type they define. If they don't, they will end up with a ClassCastException at runtime, but you avoid calling code that knows what it is doing from explicitly casting.
It is debatable if that is a good idea.
So the short answer is that you can define the Class to be whatever you want - the compiler will accept anything - but if the method doesn't return the correct type, you will get an exception at runtime, so it is all about how you implement the method. Rarely can a method be smart enough to return the right thing without the correct type as a parameter. And if you can't pass in an appropriate parameter declared with the generic type to the method, you will have to deal with a compiler warning in order to return anything (other than null).

What is the purpose of List<Void>?

I didn't even know this was doable, but I saw while perusing some code online a method with a signature like this:
public List<Void> read( ... )
... What? Is there ever a reason to do this? What could this List even hold? As far as I was aware, it's not possible to instantiate a Void object.
It is possible that this method signature was created as a by-product of some generic class.
For example, SwingWorker has two type parameters, one for final result and one for intermediate results. If you just don't want to use any intermediate results, you pass Void as the type parameter, resulting in some methods returning Void - i.e. nothing.
If there were a method List<V> returnAllIntermediateResults() in SwingWorker with Void as the type parameter V, it would have created a method just like you posted in your question.
The code would be perfectly valid. You can instantiate any implementation of the List interface (e.g. ArrayList) with type parameter Void. But the only value a Void type can have is null. So the list could not hold anything else but nulls, if the implementation allows null elements.
One case in which it may be useful is if you wanted to return a collection of return values from a function. Say
static List<T> forEach(Func<A,T> func, List<A> items) {
List<T> ret = new List<T>();
for(int i = 0; i< items.length; i++) {
ret.add(func.call(items[i]);
}
return ret;
}
public static void main() {
...
List<Void> boringResult =
forEach(
new Func<Void, Integer> {#override Void call(Integer i) {...}});
}
Not that useful but you could see a case where it was required.
List<Void> is weird. It can only have null elements, since you can't create an object of type Void. I don't think there is a practical use for such a thing.
Void is part of java.lang. It's not a special keyword or anything. It's a "pseudo-type" (according to the docs) used to as a place-holder to represent the Class object corresponding to void, as in Class<Void>. From the docs for Class:
The primitive Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects.
The Void class exists mainly for the sake of the last part of this, so you can write:
Class<Void> voidType = void.class; // == Void.TYPE
just like you can write:
Class<Integer> intType = int.class; // == Integer.TYPE
I agree, it's odd.
I can see a use for it if you want to extend a generic class and return void from a method. I've bumped into a case were I want to use int and had to use Integer because java generics don't like primitive types.
public interface ObjectUserPool<E, T> {
public E useObject(T o);
}
public class NonReturningObjectUserPool extends ObjectUserPool<Void, Integer> {
public Void useObject(Integer i);
}
I think this is what the java API is saying, though to be honest I can't really find a use for NonReturningObjectUserPool.

Categories

Resources