I am having hard time to understand the difference between two declarations on java generics.
lets say I have the following two interfaces
#FunctionalInterface
public interface CheckedFunction<T, R, E extends Throwable> {
R apply(T t) throws E;
}
public interface SomeInterface<DTO, E2 extends Throwable> {
<E extends Throwable> CheckedFunction<Object, String, E> firstFunction();
CheckedFunction<String, DTO, E2> secondFunction();
}
So now I created an implementation of those and an "unchecked" warning popped which is what I am trying to understand.
The implementation is the following
public class TmpImpl implements SomeInterface<TmpObj, IOException> {
final ObjectMapper objectMapper = new ObjectMapper();
#Override
public CheckedFunction<Object, String, JsonProcessingException> firstFunction() {
return objectMapper::writeValueAsString;
}
#Override
public CheckedFunction<String, TmpObj, IOException> secondFunction() {
return json -> objectMapper.readValue(json, TmpObj.class);
}
public static class TmpObj {
String s;
}
}
for this example I am using com.fasterxml.jackson.databind.ObjectMapper as it does exactly what I want and generates the warning.
So the firstFunction now has a warning
Unchecked overriding: return type requires unchecked conversion. Found 'CheckedFunction<java.lang.Object,java.lang.String,com.fasterxml.jackson.core.JsonProcessingException>', required 'CheckedFunction<java.lang.Object,java.lang.String,E>
while the second is just fine.
Why is this happening ? I am clearly missing something which I haven't noticed in order to fix it!
note JsonProcessingException extends IOException (declaration: public class JsonProcessingException extends IOException )
The short answer is that your implementation is dictating JsonProcessingException whereas the interface allows for E to be inferred.
In other words, according to the interface, this should be possible:
SomeInterface<String, RuntimeException> runtimeExceptionSomeInterface = null;
CheckedFunction<Object, String, RuntimeException> function =
runtimeExceptionSomeInterface.firstFunction();
This declaration:
<E extends Throwable> CheckedFunction<Object, String, E> firstFunction();
allows the caller to specify the concrete argument for E, which is exactly what I did in the above example (using RuntimeException).
However, your implementation is statically forcing JsonProcessingException:
public CheckedFunction<Object, String, JsonProcessingException> firstFunction() {
return objectMapper::writeValueAsString;
}
That is to say you've disregarded the incoming E type argument and forcefully substituting it with JsonProcessingException.
In short, if you want the firstFunction() to be generic, then implement it accordingly:
#Override
public <E extends Throwable> CheckedFunction<Object, String, E> firstFunction() {
return objectMapper::writeValueAsString;
}
However, the above fails return objectMapper::writeValueAsString; with an unhandled JsonProcessingException, which I suspect led to your implementation.
That simply means that your use of a generic Throwable parameter is wrong.
A first attempt at solving it would be to use SomeInterface's E2 variable:
interface SomeInterface<DTO, E2 extends Throwable> {
CheckedFunction<Object, String, E2> firstFunction();
CheckedFunction<String, DTO, E2> secondFunction();
}
which will solve the problem if implemented with:
class TmpImpl implements SomeInterface<TmpObj, IOException> {
//...
#Override
public CheckedFunction<Object, String, IOException> firstFunction() {
return objectMapper::writeValueAsString;
}
However, you would still have a problem because it's not the user of SomeInterface that knows what exception will be thrown, but the implementation. This means that you should declare the exception type on the API, rather than making it a generic argument:
interface SomeInterface<DTO> {
CheckedFunction<Object, String, IOException> firstFunction();
//...
}
class TmpImpl implements SomeInterface<TmpObj> {
//...
#Override
public CheckedFunction<Object, String, IOException> firstFunction() {
return objectMapper::writeValueAsString;
}
}
Which is to say that SomeInterface and its implementations know the range of exceptions to be dealt with, therefore the functions they return know of, in this case, IOException (which is selected because you know it includes JsonProcessingException, among other types)
Related
Here's a visual of the problem:
As can be seen from the visual, the IDE is showing a compile-time error to which it does not allow the class to be inserted into the Map.
Here's a simplified version:
#Override
public <T extends Comparable> void transactPersistentEntityStore(...) {
Map<Class<T>, ComparableBinding> propertyTypeMap = new HashMap<>();
propertyTypeMap.put(EmbeddedArrayIterable.class, EmbeddedEntityBinding.BINDING);
propertyTypeMap.put(EmbeddedEntityIterable.class, EmbeddedEntityBinding.BINDING);
// ...
}
Even if both EmbeddedArrayIterable and EmbeddedEntityIterable implements Comparable
Am I missing or misunderstanding something on generics?
You can simplify the point of the problem to this code snippet:
public <T extends Comparable> void m1(T x) {
Class<? extends Comparable> x1Class = x.getClass();
Class<T extends Comparable> x2Class = x.getClass();
}
Or even to this:
public <T> void m2(T x) {
Class<?> x1Class = x.getClass();
Class<T> x2Class = x.getClass();
}
The line with the variable x2Class has an error in these methods.
This is because the compiler throws away the Generics and thus there is no type T at runtime. T is not reifiable. You cannot obtain the type T at runtime.
Have also a look at this article: Why following types are reifiable& non-reifiable in java?
I created a "producer" interface (to be used with method references, respectively to be easily mocked for unit tests):
#FunctionalInterface
public interface Factory<R, T, X extends Throwable> {
public R newInstanceFor(T t) throws X;
}
which I created like that, as my first use case actually had to throw some checked WhateverException.
But my second use case doesn't have an X to throw.
The best I could come up with to make the compiler happy is:
Factory<SomeResultClass, SomeParameterClass, RuntimeException> factory;
That compiles, and does what I need, but still ugly. Is there a way to keep that single interface, but not provide an X when declaring specific instances?
You cannot do that in Java. The only way is to create a sub interface.
public interface DefaultExceptionFactory<R, T>
extends Factory<R, T, RuntimeException>
The only way to do it is subclassing - but I bet you knew that. To make my argument stronger, look at BinaryOperator that extends BiFunction.
This is more of a "social engineering" answer: we place a contract on the lambda form that it doesn't throw anything:
public interface Factory<T, R, X> {
public R newInstanceFor(T arg) throws X;
public static Factory<R, U, AssertionError> neverThrows(Factory<U, V, ?> input) {
return u -> {
try {
return input.newInstanceFor(u);
}
catch(Throwable t) {
throw new AssertionError("Broken contract: exception thrown", t);
}
};
}
}
Usage is like this, or something along the lines of:
class MyClass {
Factory<MyInput, MyOtherClass, AssertionError> factory;
MyClass(Factory<MyInput, MyOtherClass, ?> factory) {
this.factory = Factory.neverThrows(factory);
}
public void do() {
factory.newInstanceFor(new MyInput()).do();
}
}
Downside of this approach: you can't really specify the contract in the type signature, the contract is then an implementation detail. If you want to have this in type signature, you will need a second sub-interface.
You can define the method as generic like below code, if it is possible for you:
#FunctionalInterface
public interface Factory<R, T> {
public <X extends Throwable> R newInstanceFor(T t) throws X;
}
You can use Project Lombok's #SneakyThrows annotation:
#FunctionalInterface
public interface Factory<R, T> {
#SneakyThrows
R newInstanceFor(T t);
}
This allows you to throw any exception (checked or unchecked). But read the documentation because this feature must be handled with care.
Do you have to make the exception generic? Why not define the interface as
#FunctionalInterface
public interface Factory<R, T> {
public R newInstanceFor(T t) throws Throwable;
}
You can always catch your exception and check the type if you need in your calling function.
I have a method createFoo() that creates instances of Foo<T> using the Class<T> instance for that T. Now I want to extend that method to forward calls that are made using an enum type to the method createEnumFoo(). Calling the second method from the first one seems to be non-trivial. Below is an example of how I managed to do it using two unchecked casts and an extra method, all of which I would like to get rid of.
The method castEnumType() is required because I couldn't find a way to cast a Class<?> to a Class<E extends Enum<E>> without having the E bound somewhere. This involves an unchecked cast because I have not found a way to do it using Class.asSubclass(). After creating the instance of Foo, I need to cast it from Foo<E> to Foo<T> event though E and T will always be the same types.
I can't weaken the signature of createEnumFoo() because it is calling Enum.valueOf(enumType, ...) and requires the result of this to be of type E.
final class Example {
<E extends Enum<E>> Foo<E> createEnumFoo(Class<E> enumType) {
// This makes use of e.g. Enum.valueOf(enumType, ...).
return null;
}
<E extends Enum<E>> Class<E> castEnumType(Class<?> enumType) {
return (Class<E>) enumType;
}
<T> Foo<T> createFoo(Class<T> type) {
if (Enum.class.isAssignableFrom(type))
return (Foo<T>) createEnumFoo(castEnumType(type));
else
// Here we would do something else or maybe throw an exception.
return null;
}
interface Foo<T> {
}
}
Is there a simpler way to do this?
Some context
To clarify the problem I'm facing, I'll explain how this problem actually arose in a project I'm working on:
In the code where I came across this problem, Foo<T> is actually Converter<T>, which is an interface which allows an instance of T to be serialized and de-serialized from and to a JSON value:
public interface Converter<T> {
JsonObject encode(T value);
T decode(JsonObject data);
}
And createFoo() is actually a method converterForType() which takes a Class<T> instance and dynamically dispatches to a bunch of static methods and fields that create/contain converters for common Java types and types specific to the project. Normally when a converter is needed, the appropriate method/field is accessed directly but there are some places where the type is only known at runtime, which is where converterForType() is used.
Now I wanted to extend that method to automatically handle enum types by converting those to JSON strings containing the name of the enum constant. This is why I need to call the method enumConverter() from converterForType(). This is the implementation of enumConverter():
public static <E extends Enum<E>> Converter<E> enumConverter(final Class<E> enumClass) {
return new Converter<E>() {
public JsonObject encode(E value) {
return Json.convert(value.name());
}
public E decode(JsonObject data) {
return Enum.valueOf(enumClass, data.asString());
}
};
}
What about this, use raw types for createEnumFoo method
Edit: fixed compile error reported by #Feuermurmel in comments
#SuppressWarnings({ "unchecked", "rawtypes" })
final class Example
{
<E extends Enum<E>> Foo<E> createEnumFoo(Class enumType)
{
// This makes use of e.g. Enum.valueOf(enumType, ...).
Enum x = Enum.valueOf(enumType, "x");
return (Foo<E>) x;
}
<T extends Enum> Foo<T> createFoo(Class<T> type)
{
if (Enum.class.isAssignableFrom(type))
return (Foo<T>) createEnumFoo(type);
else
// Here we would do something else or maybe throw an exception.
return null;
}
interface Foo<T>
{
}
}
interface Message<T, L> {
}
interface Foo<T> {
void frob(Message<T, Foo<?>> message);
}
class AuxiliaryFoo implements Foo<Integer> {
#Override
public void frob(Message<Integer, Foo<?>> message) { }
}
class MainFoo implements Foo<Object> {
#Override
public void frob(Message<Object, Foo<?>> message) {
new AuxiliaryFoo().frob(new Message<Integer, MainFoo>() {});
}
}
The Java compiler tells me that actual argument < anonymous Message< Integer, MainFoo >> cannot be converted to Message< Integer, Foo< ?>> by method invocation conversion.
Why?
And what can be converted to Message< Integer, Foo< ?>> ?
You should use <? extends Foo<?>>
interface Foo<T> {
void frob(Message<T, ? extends Foo<?>> message);
}
Also, something worthy to keep in mind when dealing with generics is the PECS rule: Producer Extends, Consumer Super though it doesn't directly belong here, but I can't say that it doesn't belong here at all..
I want to defined a generic parameter, which should extend Map or Collection, but I don't know how to do it:
public <T> void test(T t) {}
I can write it as:
public <T extends Map> void test(T t) {}
or
public <T extends Collection> void test(T t) {}
But I don't know is it possible to let T extend Map or Collection in a single method.
Short answer: no.
What do you intend to do with the t parameter within the method? Since Map and Collection have only Object as their common supertype, the only methods you can call on t will be those on Object. (Even methods on both interfaces, such as size(), will be rejected by the compiler.)
With that in mind, is there any reason you can't overload the method in this case? That is, define one implementation for each desired parameter type:
public void test(Map<?,?> t) { ... }
public void test(Collection<?> t) { ... }
If you don't want to do that for whatever reason, then it seems that you could equally just declare the method to take Object and perform a run-time type check of the class. This is semantically equivalent since you'd have to cast to call Map or Collection-specific methods anyway, though it does mean that the type check is done at compile time instead of runtime. Java's type system doesn't let you express this union-type dependency though, so I don't see any other alternative.
No it is not possible, but you can create two separate methods:
public <T extends Map> void test(T t) {
// do the map part
}
public <T extends Collection> void test(T t) {
// do the collection part
}
If you want to mix them in a handling method you can also write it like this:
private void mixedTest(Object t) {
if (t instanceof Map) {
// map part
} else if (t instanceof Collection) {
// collection part
} else {
throw new RuntimeException("Unknown object");
}
}
and call:
public <T extends Map> void test(T t) {
mixedTest(t);
}
public <T extends Collection> void test(T t) {
mixedTest(t);
}
But I'm not sure it will lead to a nice code anyway. I would stick with the first part with the different implementation for the different type of objects.
What about
public <T extends Map> void test(Map t) {}
and
public <T extends Collection> void test(Collection t) {}
...and then let Java choose the correct one to use?