Can't resolve this Java generics compile-time warning - java

Please consider the following code:
public abstract class Subject {
private Collection<Observer> observerCollection = new HashSet<>();
// ...
protected void notifyObservers() {
this.observerCollection.stream().filter(Objects::nonNull).forEach(o -> o.update(this));
}
}
public interface Observer<T extends Subject> {
void update(T subject);
}
I am getting the following compile-time warnings:
Observer is a raw type. References to generic type Observer should be parameterized
Type safety: The method update(Subject) belongs to the raw type Observer. References to generic type Observer should be parameterized
One comes at the call to update and for the life of me I can't figure out how to resolve it without using the warning suppressions. I've tried several ways to resolve the warning without luck. Any ideas on how this can be resolved?
Motivation
Consider the following client code:
public class IntegerContainer extends Subject {
private int integer;
public IntegerContainer(int integer) {
this.integer = integer;
}
public int getInteger() {
return this.integer;
} // ...
}
public class IntegerObserver implements Observer<IntegerContainer> {
private int cachedInteger;
#Override
public void update(IntegerContainer subject) {
this.cachedInteger = subject.getInteger(); // avoid cast here.
} // ...
}
The motivation for using generics in the Observer is to avoid a cast of the subject parameter so that the observer can retrieve the state of the subject.

This doesn't have anything to do with streams; it just straight up won't work.
An Observer<? extends Subject> is more or less unusable, because you don't know what subtype of Subject it's an observer of. For all you know, observerCollection only contains an Observer<SomeSubtypeOfSubjectThatNobodyEvenHeardOf>. (See the PECS principle; Observer is a consumer.)
I don't think there's any type-safe way to do this cleanly, frankly, because you can't say in Subject that the attached observers all accept this subtype of Subject, because there's no way to refer to "this subtype of Subject." The closest hack I can think of is
abstract class Subject<T extends Subject<T>> {
private Collection<Observer<? super T>> observers;
protected void notifyObservers() {
this.observerCollection.stream().filter(Objects::nonNull).forEach(o -> o.update((T) this)); // yes, this cast is unchecked
}
}
class SubSubject extends Subject<SubSubject> {
...
}

I'd focus on the value being passed between the Subject and Observer. I.e. both classes have one type parameter and the related methods make sure that the types are compatible:
public interface Observer<T> {
void update(T value); // no subject, but a value
}
public class Subject<T> {
private Collection<Observer<? super T>> observers = new HashSet<>();
protected void notifyObservers() {
this.observers.stream().filter(Objects::nonNull).forEach(o -> o.update(this.getValue()));
}
public void addObserver(Observer<T> observer) { // adding the right kind of observer
observers.add(observer);
}
abstract public T getValue(); // returning the value - this one is abstract
}
The key above is the abstract public T getValue(); method. Here is how you can write an IntegerContainer and and IntegerObserver :
public class IntegerContainer extends Subject<Integer> {
private int value;
public IntegerContainer(int value) {
this.value = value;
}
#Override
public Integer getValue() {
return value; // this is the parameter of the update() call
// you could even compute here something
// you can pass entire objects too, if required
}
}
public class IntegerObserver implements Observer<Integer> {
private int cachedInteger;
#Override
public void update(Integer value) {
this.cachedInteger = value; // no cast here
} // ...
}
You can put them together like this:
IntegerContainer container = new IntegerContainer(3);
IntegerObserver observer = new IntegerObserver();
container.addObserver(observer);
container.notifyObservers();

Related

Java generic List with genric items as parameter

I have the following interface with generic and an implentation class:
public interface DataInterface<T> {
T getData();
void printData();
}
public class IntegerData implements DataInterface<Integer> {
private Integer value;
public IntegerData(Integer value) {
this.value = value;
}
#Override
public Integer getData() {
return null;
}
#Override
public void printData() {
System.out.println(this.value);
}
}
And here my code that use the class:
public class Main {
public static void main(String[] args) {
List<IntegerData> dataList = new ArrayList<>();
dataList.add(new IntegerData(1));
doSomething(dataList); <-- Compiler error
//this work
doSomething(Collections.unmodifiableList(dataList));
doSomething(new ArrayList<>(dataList));
}
private static void doSomething(List<DataInterface<?>> dataList) {
for (DataInterface<?> data : dataList)
data.printData();
}
}
If I try to call the doSomething method with the List type, the compiler complains with "The method doSomething(List<DataInterface<?>>) in the type Main is not applicable for the arguments (List)".
But if I wrap my specific list or create a new one, then it works. I wonder why the direct call doesn't work. What is the reason?
A List<IntegerData> or List<DataInterface<Integer>> is not compatible with List<DataInterface<?>>, because I can also add a DataInterface<String> to the latter. The solution is to use extends:
private static void doSomething(List<? extends DataInterface<?>> dataList)
This will prevent adding anything to the list (except null), and is therefore safe to use.
By wrapping the list, the generic type is changed due to type inference. If you'd assign it to a variable using var it would also fail.

Java generics both methods have same erasure error

In my project, I have multiple services performing three basic operations - create, edit and search. For this, I am trying to create a generic service. Below is what I have come up with so far.
Search method will take a list of objects at runtime.
public interface GenericService<T> {
void update(T t);
void create(T t);
T search(List<?> t);
}
Also, I have created an abstract class where the common code for all services will be placed.
public abstract class AbstractService<T> implements GenericService<T> {
}
Here is my implementation
public class AccountService extends AbstractService<Account> implements GenericService<Account> {
#Override
public void update(Account account) { }
#Override
public void create(Account account) { }
#Override
public Account search(List<SearchCriteria> t) { return null; }
}
Here are my Account and SearchCriteria classes
public class Account {
private String accountNumber;
private Date openingDate;
// more fields
// getter setter removed for brevity
}
Search criteria class
public class SearchCriteria {
private String key;
private String value;
// getter setter removed for brevity
}
Problem: on line public Account search(List t) { return null; }, getting compilation error saying
'search(List)' in
'com.test.AccountService' clashes with
'search(List)' in 'com.test.GenericService';
both methods have same erasure, yet neither overrides the other
In order for
public Account search(List<SearchCriteria> t) { ...}
to override
T search(List<?> t);
The arguments must be the same after type parameter substitution, but ? is not SearchCriteria.
Therefore, if you want to keep these methods (the inheritance looks a bit wild to me), you'll need to parameterise the types further.
public interface GenericService<T, C> {
// ...
T search(List<C> t); // probably change that parameter name
}
public abstract class AbstractService<T, C>
implements GenericService<T, C>
{
}
public class AccountService
extends AbstractService<Account, SearchCriteria>
implements GenericService<Account, SearchCriteria> // unnecessary
{
// ...
#Override
public Account search(List<SearchCriteria> t) { /* ... */ }
}
Changing List<?> to List<SearchCriteria> in GenericService will solve the error. There is no benefit in using a wildcard if the search method will always take a list of SearchCriteria objects in every service implementation.
If, however, you want to make this generic as well, you can introduce a second type parameter.

Infer generic class type parameters from single constructor parameter

What I want is following:
Most of the time, the generic class will be like TestBuilder<X, X>, meaning that T and O are of the same type. Therefore I create two different constructor. I want to make anonoumous new calls like new TestBuilder<>(...) (I'm calling the <> anonoumous here).
Following 4 constructor example exists:
1) Working constructor calls
// Anonoumous, working
new TestBuilder<>(String.class, Integer.class)
.withOnNext(new Action1<Integer>()
{
#Override
public void call(Integer integer)
{
}
});
// not anonoumous, classified, working
new TestBuilder<String, String>(String.class)
.withOnNext(new Action1<String>()
{
#Override
public void call(String string)
{
}
});
2) Constructor Calls with problems or not working
// Anonoumous and working
// PROBLEM: withOnNext is called with Object instead of String
new TestBuilder<>(String.class)
.withOnNext(new Action1<Object>()
{
#Override
public void call(Object o)
{
}
});
// Anonoumous and NOT working
// this is what I want to work!
new TestBuilder<>(String.class)
.withOnNext(new Action1<String>()
{
#Override
public void call(String string)
{
}
});
Question
Is there a way to get the 4th constructor to work? I don't want to be forced to give the constuctor two classes if I call it with one argument only, the second generic class should "inherit" from the first in this case... Instead of having to write new TestBuilder<String, String>(String.class) I want to write new TestBuilder<>(String.class) or at least new TestBuilder<String>(String.class)...
Class
This is what the test builder class looks like:
public class TestBuilder<T, O>
{
public TestBuilder(Class<T> eventClass)
{
this(eventClass, (Class<O>)eventClass);
}
private TestBuilder(Class<T> eventClass, Class<O> observableClass)
{
init();
}
public TestBuilder<T, O> withOnNext(Action1<O> actionNext)
{
mActionNext = actionNext;
return this;
}
}
I don't think Java can infer the second generic type without some kind of hint. One way is giving the type in variable declaration:
TestBuilder<String, String> testBuilder = new TestBuilder<>(String.class);
testBuilder.withOnNext(new Action1<String>() {
#Override
public void call(String string) {
//...
}
});
But you'd still need to declare both generic parameters.
What I would do is encapsulating the information that both T and O are the same in a static factory method:
public class TestBuilder<T, O> {
public static <T> TestBuilder<T, T> create(Class<T> eventClass) {
return new TestBuilder<T, T>(eventClass);
}
// ...
}
and then call it like this:
TestBuilder.create(String.class).withOnNext(...);
Yet another option is encapsulating the information in a separate class inheriting from TestBuilder:
public class SimpleTestBuilder<T> extends TestBuilder<T,T> {
public SimpleTestBuilder(Class<T> eventClass) {
super(eventClass, eventClass);
}
}
public class TestBuilder<T, O> {
private TestBuilder(Class<T> eventClass, Class<O> observableClass) {
}
// ...
}
Used as
new SimpleTestBuilder<>(String.class).withOnNext(...);
Yet another good option is to encapsulate the information O is same as T in a static method:
public class TestBuilder<T, O> {
public static <T> TestBuilder<T, T> create(Class<T> eventClass) {
return new TestBuilder<T, T>(eventClass);
}
// ...
}
Used as
TestBuilder.create(String.class).withOnNext(...);
You can introduce a helper type variable for the first constructor like this:
public class TestBuilder <T, O>
{
public <H extends T, O> TestBuilder(Class<H> c)
{
this((Class) c, (Class) c);
}
public TestBuilder(Class<T> c1, Class<O> c2)
{
// ...
}
public static void main(String[] args)
{
TestBuilder<String, String> hw = new TestBuilder<>(String.class);
System.out.println(hw);
}
}
This will create some unchecked warnings for the constructor, but not at the call site. Please note though that some might consider this ad practise, especially since not everyone knows about constructor type parameters. For the sake of completeness, the explicit invocation of the constructor has to look like this:
new<String> TestBuilder<>(String.class).doStuff()

Java Generics and unchecked cast

I'm struggling with this aspect of Generics in Java. Hopefully someone can help me see the ways.
I have a class that holds a List of objects. This code works, but I want to get rid of the cast. How can I make this more generic?
public class Executor {
List<BaseRequest<BaseObj>> mRequests = new ArrayList<BaseRequest<BaseObj>>();
public Executor() {
}
#SuppressWarnings("unchecked")
public <T extends BaseObj> void add(final BaseRequest<T> request) {
mRequests.add((BaseRequest<BaseObj>) request);
}
public void execute() {
for (BaseRequest<BaseObj> r : mRequests) {
// DO SOMETHING WITH r
}
}
}
In the posted snippet you need the cast because BaseRequest<? extends BaseObj> is not a subtype of BaseRequest<BaseObj>, and the cast can't be checked at runtime because of type erasure, and that's why the compiler warns you. But if you change the declaration of mRequests:
public class Executor {
List<BaseRequest<? extends BaseObj>> mRequests = new ArrayList<>();
public Executor() {
}
public <T extends BaseObj> void add(final BaseRequest<T> request) {
mRequests.add(request);
}
public void execute() {
for (BaseRequest<? extends BaseObj> r : mRequests) {
// DO SOMETHING WITH r
}
}
}
class BaseRequest<T> {}
class BaseObj {}
Let's resolve the problem step-by-step. You want to be able to call
req.add(new BaseRequest<ExtObj1>());
req.add(new BaseRequest<ExtObj2>());
req.add(new BaseRequest<ExtObj3>());
where ExtObj[1|2|3] extends BaseObj. Given the List interface:
List<T> {
void add(T el);
}
we need to find a common supertype for BaseRequest<ExtObj1>, BaseRequest<ExtObj2> and BaseRequest<ExtObj3>. One supertype is BaseRequest<?> and another one is BaseRequest<? extends BaseObj>. I picked the second one because it's the most restrictive possible. You should know that in Java BaseRequest<ExtObj1> is not a subtype of BaseRequest<BaseObj> because generics are invariant.
Now that we have the right declaration for mRequests, finding the API for Executor.add() is straightforward. BTW, if the method body you need is really that simple, you don't even need the type parameter:
public void add(BaseRequest<? extends BaseObj> request) {
mRequests.add(request);
}
Warnings are not errors. Warnings are there so you check if you have an error because it may not be checked automatically. You should check it and then use the annotation to note that warning was already checked.
In your case it warns BaseRequest<T> is not equivalent to BaseRequest<BaseObj>.
Example:
public class NumberWrapper<N extends Number> {
private N value;
public void setValue(N value) {
this.value = value;
}
}
public class MainClazz {
private NumberWrapper<Integer> wrappedNumber = new NumberWrapper<Integer>();
public void run() {
Number n = externalSource.getNumber();
wrappedNumber.setValue(n); // <-- Error. What if getNumber returns a double?
}
}
You can have this error ir not depending on how you complete/integrate the code you are showing.

How to get the class of type variable in Java Generics

I've seen similar questions but they didnt help very much.
For instance I've got this Generic Class:
public class ContainerTest<T>
{
public void doSomething()
{
//I want here to determinate the Class of the type argument (In this case String)
}
}
and Another Class which uses this Container Class
public class TestCase
{
private ContainerTest<String> containerTest;
public void someMethod()
{
containerTest.doSomething();
}
}
Is it possible to determinate the Class of the type argument in method doSomething() without having an explicit type variable/field or any constructor in ContainerTest Class?
Update: Changed format of ContainerTest Class
The only way is to store the class in an instance variable and require it as an argument of the constructor:
public class ContainerTest<T>
{
private Class<T> tClass;
public ContainerTest(Class<T> tClass) {
this.tCLass = tClass;
}
public void doSomething()
{
//access tClass here
}
}
If you are interested in the reflection way, I found a partial solution in this great article: http://www.artima.com/weblogs/viewpost.jsp?thread=208860
In short, you can use java.lang.Class.getGenericSuperclass() and java.lang.reflect.ParameterizedType.getActualTypeArguments() methods, but you have to subclass some parent super class.
Following snippet works for a class that directly extends the superclass AbstractUserType. See the referenced article for more general solution.
import java.lang.reflect.ParameterizedType;
public class AbstractUserType<T> {
public Class<T> returnedClass() {
ParameterizedType parameterizedType = (ParameterizedType) getClass()
.getGenericSuperclass();
#SuppressWarnings("unchecked")
Class<T> ret = (Class<T>) parameterizedType.getActualTypeArguments()[0];
return ret;
}
public static void main(String[] args) {
AbstractUserType<String> myVar = new AbstractUserType<String>() {};
System.err.println(myVar.returnedClass());
}
}
There is no "clean" way to get the Generic Type argument from within the class.
Instead, a common pattern is to pass the Class of the Generic Type to the constructor and keep it as an inner property juste as done in the java.util.EnumMap implementation.
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/EnumMap.html
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/EnumMap.java
public class ContainerTest<T> {
Class<T> type;
T t;
public ContainerTest(Class<T> type) {
this.type = type;
}
public void setT(T t) {
this.t = t;
}
public T getT() {
return t;
}
public void doSomething() {
//There you can use "type" property.
}
}
No. It is not possible because of type erasure (the type parameters are compiled as Object + type casts). If you really need to know/enforce the type in runtime you may store a reference to a Class object.
public class ContainerTest<T> {
private final Class<T> klass;
private final List<T> list = new ArrayList<T>();
ContainerTest(Class<T> klass) {
this.klass = klass;
}
Class<T> getElementClass() {
return klass;
}
void add(T t) {
//klass.cast forces a runtime cast operation
list.add(klass.cast(t));
}
}
Use:
ContainerTest<String> c = new ContainerTest<>(String.class);
There is a way to get the runtime type of the type parameter by using Guava's TypeToken to capture it. The solution's disadvantage is that you have to create an anonymous subclass each time you need an instance of Container.
class Container<T> {
TypeToken<T> tokenOfContainedType = new TypeToken<T>(getClass()) {};
public Type getContainedType() {
return tokenOfContainedType.getType();
}
}
class TestCase {
// note that containerTest is not a simple instance of Container,
// an anonymous subclass is created
private Container<String> containerTest = new Container<String>() {};
#Test
public void test() {
Assert.assertEquals(String.class, containerTest.getContainedType());
}
}
The key of this solution is described in tha JavaDoc of TypeToken's constructor used in the code above:
Clients create an empty anonymous subclass. Doing so embeds the type parameter in the anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
If You can define like this
public class ContainerTest<T>
{
public void doSomething(T clazz)
{
}
}
Then it is possible

Categories

Resources