I haven't used generics before and I am wondering when I should use them and what the advantages are. I think it might be appropriate for a collection that I made since java always uses generics for collections as well but if I call the methods I created the type is already set in the function so it would give an error anyway. When should I use a generic class? Could you give an example because I am not sure how to use it. At the moment my code is as follows:
public class NodeList {
private static final int MAX_AMOUNT_OF_NODES = 12;
private HashMap<String, Node> nodeList;
public NodeList(){
nodeList = new HashMap<String, Node>(MAX_AMOUNT_OF_NODES);
}
public Node get(String id){
return nodeList.get(id);
}
public boolean add(Node node){
if(nodeList.size() <= MAX_AMOUNT_OF_NODES){
nodeList.put(node.id, node);
return true;
}
return false;
}
}
You can look at the existing API for guidance. For example, all the Collections are generic. That is because all collections contain elements of a type.
From that, it makes sense that generic classes should be used when you would have to create the exact same code again and again for different types. If you have to do that, generics might offer you some benefit.
As far as an example, the docs are a good place to start.
From that link, the first code sample is
public class Box<T> {
// T stands for "Type"
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
}
Conceptually, there is a Box class that is going to contain something. What it contains does not matter, because the type is specific by the programmer. A Box instance can contain basically anything. When the programmer needs to create a box, he/she specifies the type.
Box<SomeClass> myBox = new Box<SomeClass>();
Think about it this way -- if you wanted to create a general Box that could hold anything without generics, you would have to
1) have the field f be an Object, or
2) create a Box class for every type a box could contain.
With generics, you only need one class, and you can specify the exact type. Maybe if you are doing something and your approach involved either 1 or 2 above, it's better to use generics.
If Node is a class that can hold a piece of data with certain type (like String, for example) then you should generify Node and subsequently NodeList to prevent type errors.
If you don't, then you leave it up to the user of your NodeList to ensure that she never adds an Integer when the list is only supposed to hold Strings. Generics is primarily about catching type problems at compile time rather than runtime.
It's pretty simple to do so, change something like this:
public class Node {
Object data;
//...
}
to something like this:
public class Node<T> {
T data;
//...
}
public class NodeList<T> {
public Node<T> get(String id) {
//...
}
public boolean add(Node<T> node) {
//...
}
}
Your NodeList looks like it could potentially have a second type parameter for the key type, which right now you're constraining to String.
You can generically type the methods arguments as well as the class itself. Here's an example from Java's java.util.List interface:
public interface List<E> {
//...
boolean add(E e);
//...
}
Generics are a way for Java to force a collection data structure (HashMap in your case) to accept only a specific types of objects. This means that at compile time, if you tried something like:
nodeList.add(1, new Node());
it would fail and not compile since 1 is not a String object. It is generally a way to write tidier code.
Check this link as well:http://en.wikipedia.org/wiki/Generics_in_Java
Related
I have no particular use for this in mind, but is it possible to write a method that accepts any number of nested lists in Java?
I got as far as this:
private <T extends List<? extends T>> void masterOfLists(final T list) {
}
The small issue with this now is that it never ends. I neither want to lose generics, so simply accepting an Object and try casting it to a List every pass is not an option in my question.
I hoped it would be clear enough, but appereantly it isn't for some, I want the method masterOfLists to accept the following examples (and way more):
masterOfLists(new ArrayList<Object>())
masterOfLists(new ArrayList<List<Object>>())
masterOfLists(new ArrayList<List<List<Object>>>())
masterOfLists(new ArrayList<List<List<List<Object>>>>())
Instead of Object it may also be a concrete type like String.
The used List may be any type of list, like ArrayList or LinkedList or your custom implementation.
Using pure List won't help you here, you need to define a recursive class.
As an additional source of inspiration you can take a look at my code for Recursive Tic-Tac-Toe
You could create a class something like this:
public class Recursive<T> {
List<Recursive<T>> sub;
T value;
boolean hasSub() {
return sub != null;
}
T getValue() {
return value;
}
void forEach(Consumer<T> t) {
if (hasSub())
sub.forEach(t);
else t.accept(value);
}
}
You can use logic in this class to prevent it from both having a sub-list and an actual value, using constructors and/or setters.
And then if you want to iterate over it and print out all the sub-items recursively, you can use
Recursive<T> recursive;
recursive.forEach(System.out::println);
Then your method can look like this:
private <T> void masterOfLists(final Recursive<T> list) {
You won't get anywhere using pure Lists because the generic type of the list is not available at runtime, and the generics will only create a mess for you here. Using a recursive class is much easier.
The 'cheap' solution is to extend the ArrayList class with your own name, and force the Generics on the subclass. The SubClass is still an ArrayList....:
public class NestingList extends ArrayList<NestingList> {
// all we do is set the Generics...
}
I'm trying to get familiar with generics in java. I'm still unsure how create a simple class to take two types (String, Integer). Below is a trivial attempt at working with generics in my contexts.
public class Container <T>
{
public T aString()
{
//Do i know I have a string?
}
public T anInt()
{
//How do I know I have an integer?
}
public Container<T>()
{
//What would the constructor look like?
}
}
I'm referencing this page oracle generics but I'm still not sure what I'm doing here. Do you first figure out what type your "T" in the class?
Is generic programming genuinely used for interfaces and abstract classes?
Well that Container class can actually hold a String, Integer or any type, you just have to use it correctly. Something like this:
public class Container<T> {
private T element;
public T getElement() {
return element;
}
public void setElement(T element) {
this.element = element;
}
public Container(T someElement) {
this.element = someElement;
}
}
And you can use it like this:
Container<Integer> myIntContainer = new Container<Integer>();
myIntContainer.setElement(234);
or...
Container<String> myStringContainer = new Container<String>();
myStringContainer.setElement("TEST");
If the class does significantly different things for String and Integer, maybe it should be two classes, each specialized for one of those types.
I see generics as being useful for situations in which references of different types can be handled the same way. ArrayList<String> and ArrayList<Integer> don't need any code that is specific to String or Integer.
Class type = Integer.class
Integer i = verifyType("100",type);
for integer, similar with string...
reference Java Generics with Class <T>
If you want to use String and Integer you'll probably have to use Object as the type. This removes most of the benefit of Generics frankly and you should probably check that you actually have a sound model and reason for inter-weaving strings and integers.
But yes, it's useful for interfaces, custom classes and abstracts. It means you can guarantee the object is of the right type and removes the need to implement them each time for each type of thing.
I am having a slight inconvenience when working with generics in Java. Please consider the following code:
/**
* MyElement class is simply a wrapper for a generic object.
*/
public static class MyElement<T> {
public final T OBJ;
public MyElement(T obj) {
this.OBJ = obj;
}
}
/**
* MyElementList contains an array list of MyElements of the given type, T.
* This represents a class that uses a list of MyElements of a certain type,
* and this list can be accessed in an unmodifiable format.
*/
public static class MyElementList<T> {
//Properties
private List<MyElement<T>> elementList = new ArrayList();
//CTOR
public MyElementList(List<MyElement<T>> initElements) {
elementList.addAll(initElements);
}
//Getter
public List<MyElement<T>> getElements() {
return Collections.unmodifiableList(elementList);
}
}
public static void main(String[] args) {
//New list of elements
//Notice that I did not explicitly specify the type for 'MyElement'
List<MyElement> theElements = new ArrayList(Arrays.asList(
new MyElement[] {
new MyElement("E 1"),
new MyElement("E 2"),
new MyElement("E 3")
}
));
//Also notice I did not explicitly specify the type for 'MyElementList'
MyElementList theList = new MyElementList(theElements);
//The following does not work.
//It seems to not work because theList.getElements() returns a 'List'
//not necessarily a 'List<MyElement>' which is what I would expect it to
//return...
//Why???
for(MyElement e : theList.getElements()) {
System.out.println(e.OBJ.toString());
}
//Currently my work around is to do the following, but I do not like
//having to introduce another variable, and I would rather just do the
//one above
List<MyElement> listOfElements = theList.getElements();
for(MyElement e : listOfElements) {
System.out.println(e.OBJ.toString());
}
//How come the first 'for-each' loop method does not work?
//Is there anyway I could get it to work?
//THANK YOU!
}
In the main method, if I don't specify the type parameter for 'MyElementList' the 'getElements()' method only returns a 'List', not a 'List<MyElement>'. This is inconvenient because if I want to iterate through each 'MyElement' I need to introduce another variable as a temporary list, shown in the code.
Why doesn't the 'getElements()' method return a 'List<MyElement>'?
Without making significant changes to 'MyElementList' Is there anything I can do to fix this?
Is this a bad design practice?
The IDE I am using is Netbeans 7.2
Thanks in advance!
EDIT
Thank you all for your quick responses. I am very impressed with the community here. I have concluded the following:
If a generic hint is not specified, Java ignores ALL other associated generic hints for a class - which is kind of lame, but I can live with it.
When using generics, it is a best practice to actually specify the generic type when creating an instance of the class. This seems to be the most object oriented solution.
Thanks again!
If you change MyElementList to look like
public static class MyElementList<T extends MyElement> {
//Properties
private List<T> elementList = new ArrayList<T>();
//CTOR
public MyElementList(List<T> initElements) {
elementList.addAll(initElements);
}
//Getter
public List<T> getElements() {
return Collections.unmodifiableList(elementList);
}
}
It should work.
EDIT Generics can be seen as compile time hints in Java, since Java erasure will convert generics to Object. Updating your class as above will tell the compiler only elements which extend MyElement fit the list and for(MyElement e : theList.getElements()) will work.
EDIT 2 As pointed out by others (sorry, I didn't see it at first glance) also change the raw declaration to:
MyElementList<MyElement> theList = new MyElementList<MyElement>(theElements);
Te first does not work because getElements returns a List<?> for the raw type
The second works because you assigned it to a List<MyElement>, ignoring the warning. Ignoring was ok because you know what it contains, but the compiler doesn't.
Instead of using
for(MyElement e : theList.getElements()) {
System.out.println(e.OBJ.toString());
}
you could use
for (Iterator<MyElement> it = theList.getElements().iterator(); it.hasNext();) {
MyElement e = it.next();
System.out.println(e.next().OBJ.toString());
}
which makes your compiler compliant.
But I would prefer to specify the types that your classes require when instantiating/accessing them (and your compiler too, I guess ;)).
Why doesn't the getElements() method return a List<MyElement>
Because MyElement is typed!
Without making significant changes to MyElementList Is there
anything I can do to fix this?
You can probably use a wildcard:
List<MyElement<?>> someList = getElements();
Say that I need to create 3 linked lists one for int, one for Strings and one for a different type of custom object. If I was using generics it would be easy to do this just by creating one linked list but is there a way to avoid writing the same repetetive code 3 times if I was not using generics?
If you use Integer instead of int, then yes. In this case, all three objects are subclasses of Object so your Linked List class could just deal with Objects.
The code would look, roughly, like:
class MyLinkedList{
public void add(Object){...}
public Object remove(Object){...}
...
}
Before the introduction of generics in Java 1.5 the Collections all used the type Object, so you would have a linked list of Objects. You then had to make sure yourself that you were adding, retrieving and casting the right types.
I don't see any reason why you shouldn't or wouldn't use generics since using Java 1.4 is hardly necessary or recommended anymore.
You couldn't use the same code for ints vs Strings, but assuming you meant Integers, then you would have to create a LinkedList that stored java.lang.Objects, which by the way, is what generic based LinkedLists do.
You can abstract out the type that the linked list handles into your own type, say StringOrIntOrCustom, that has one of each type, and a flag that specifies which one is the valid one to use. However, you'd need to do a lot of checking to make sure you're not doing an operation that a type does not support whenever you use this data type.
You could do it with out generics, but you would have no compile time type safety checking and you will have to add all the type casting manually.
But then again you will essentially doing what the compiler does when you use generics. Now it's just error prone and manual.
Yes, you can do this, and it's not that hard.
abstract class ListNode {
public ListNode next_;
};
interface ListNodeFactory {
public ListNode createListNode();
}
You then create a List class the manipulates ListNode objects. It will have a function it calls to create a new ListNode when it needs one. The add method would take a ListNodeFactory argument. I would suggest most of the methods be protected because no client that's not a derived class is likely to use it.
The you create a derived class from List for each type. You will have to wrap each method with one that takes the type you want. You will also have to create a ListNodeFactory implementation that creates new list nodes with the appropriate type in them. It will also have to cast the ListNode objects it gets out from traversals or removals to the appropriate types in order to get at the data. Here is an example:
class IntListNode extends ListNode {
public int data_;
public IntListNode(int x) {
data_ = x;
}
}
class IntListNodeFactory implements ListNodeFactory {
IntListNodeFactory() {
nextdataset_ = false;
}
IntListNodeFactory(int x) {
nextdataset_ = true;
data_ = x;
}
void setNextData(int x) {
nextdataset_ = true;
nextdata_ = x;
}
public ListNode createListNode() {
if (!nextdataset_) {
throw Exception("Tried to create a node with no data!");
} else {
ListNode result = new IntListNode(data_);
nextdataset_ = false;
return result;
}
}
}
I am studying Data Structures in java and I am having difficulty with using generics in Binary Search Trees.
For our assignment we are to implement a Binary Search Tree using nodes that contain a parent, left and right node as well as a data value.
The data value in our case takes the form of a Pair object. This is what it looks like:
public class Pair<A,B> {
public final A fst;
public final B snd;
public Pair(A x, B y) {
fst = x; snd = y;
}
public String toString() {
return new String("("+fst.toString()+", "+snd.toString()+")");
}
}
Pair is associtated with two different generics with the first part being the Key and the second being the Value associated with that key.
I also need to implement Iterator in my BST class. I am implementing the Iterator in an inner class that looks something like this:
public Iterator<Pair<K,T>> iterator() {
return new BSTMapIter<Pair<K,T>>(this.root, this.size, this.order);
}
private class BSTMapIter<Pair<K,T>> implements Iterator<Pair<K,T>> { <=== Compiler error "> expected"
...
... (Implementation here)
...
}
The problem I am running into is a compiler error saying "> expected" which leads to other compiler errors ("<identifier expected>" etc.). From my understanding it is choking over <Pair<K,T>> but I have no idea why. I am assuming it is a mistake I made with using generics somewhere, but I am not entirely sure where to look.
I apologize if what I have provided is too vague but I have not encountered any problems with Pair in my implementation anywhere else but here in the implementation of the Iterator.
Can anyone tell me what I am doing wrong here???
If any further information is needed, let me know and I will do my best to provide :)
The problem is the way you're trying to make BSTMapIter generic. It needs to be generic in two type parameters, K and T. The Pair part is irrelevant at this point. (It's important when it comes to what interface it implements though.) So the declaration should be:
private class BSTMapIter<K,T> implements Iterator<Pair<K,T>>
However, that's if you want BSTMapIter to be generic in itself. If this is a nested class within a type which already has K and T as type parameters, you probably just want:
private class BSTMapIter implements Iterator<Pair<K,T>>
You also want to instantiate it slightly differently:
// When it's an inner class
public Iterator<Pair<K,T>> iterator() {
return new BSTMapIter(this.root, this.size, this.order);
}
// When it's a standalone generic class
public Iterator<Pair<K,T>> iterator() {
return new BSTMapIter<K, T>(this.root, this.size, this.order);
}