I'm trying to learn how to use generics, but I am having a hard time with implementing a factory pattern. I want my DataFactory interface to have a method that returns an object of a class that extends Data. I believe the following code will work, but I'm having a hard time understanding something. Is there a way to avoid having to explicitly specify the second type in the DataFactory interface? Message extends Data<String>, so if T extends Data<U>, shouldn't the U be implied already?
Data class:
public abstract class Data<T> {
private final long id;
private final T content;
private final User sender;
...
}
Message class:
public class Message extends Data<String> {
...
}
DataFactory Interface:
public interface DataFactory<T extends Data<U>, U> {
T newInstance(U content, User sender);
}
MessageFactory Class:
public class MessageFactory implements DataFactory<Message, String> {
@Override
public Message newInstance(String content, User sender) {
return new Message(content, sender);
}
}
Why can't I just write:
public class MessageFactory implements DataFactory<Message>
I'm sorry if this is not a well worded question, I'm not exactly sure how to express it. Originally I didn't want to add type parameters to the class itself, just to the method, but I had more issues trying to make that work. I'm not even sure if I am implementing this correctly. Any help would be appreciated.
CodePudding user response:
Because the type system itself doesn't know that, in the general case, you won't write GenericData<T> extends Data<T>
. In this specific case, your Message
class has a specific class for the generic type, but you could supply other parameters that wouldn't.
CodePudding user response:
Even though the OP selected already an answer, I want to share additional, relevant information to help others that will come here looking for answers.
Why use generics?
First, we need to understand what is the reason to use generics in the first place. Generic type parameters provide a way for you to re-use the same code with different inputs. For example, you can use generic code to handle a collection of strings, or a collection of widgets with the same code. Before generics, the way we used to handle similar cases was by casting objects. For simplification, let us use OP example and make the Data
class non-abstract.
public void doSomethingWithThisInput (Object o) {
if (o instanceof MyClass) {
MyClass myCls = (MyClass) o;
// do something here
}
}
If you ever dealt with Java Collection before generics, you are aware of how painful this used to be. Part of the problem of the old approach was that, in many cases, you didn't discover issues with casting during development. Almost 100% of these cases were discovered at runtime which everyone can agree is very bad. With generics, these issues of object incompatibility are discovered at compile time.
Use cases for generics
The main reason why you would want to use generics is to create a collection of similar "things" inside a class. Examples of this can be found all over the Java language with Set
, List
, Map
, etc.
public abstract class Data<T> {
private T content;
// other methods and attributes omitted;
}
With such a class, you refer to the contents of the Data
object generically. The data content can be literally anything: An Email
object, a File
object, or simply a String
. Obviously, the intent here is that if you have a collection of data contents, each object should be of the same type. Creating a collection of disparate contents makes little sense and it is actually counterproductive.