We want a collection class Repeater
that supports the following behavior:
It would be silly to explicity store five copies of “Hello”. Here is a reasonable implementation:
public class Repeater implements Iterable<String> {
private String what;
private int howMany;
public Repeater(String what, int howMany) {
this.what = what;
this.howMany = howMany;
}
@Override
public Iterator<String> iterator() {
return new RepeatIterator();
}
private class RepeatIterator implements Iterator<String> {
int remaining = howMany; // Notice that we can access the members
// of the outer class.
@Override
public boolean hasNext() {
return remaining > 0;
}
@Override
public String next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
remaining--;
return what;
}
}
}
remove
Methodpublic class Repeater implements Iterable<String> {
private String what;
private int howMany;
public Repeater(String what, int howMany) {
this.what = what;
this.howMany = howMany;
}
@Override
public Iterator<String> iterator() {
return new RepeatIterator();
}
private class RepeatIterator implements Iterator<String> {
int remaining = howMany;
boolean deleteOk = false;
@Override
public boolean hasNext() {
return remaining > 0;
}
@Override
public String next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
remaining--;
deleteOk = true;
return what;
}
@Override
public void remove() {
// remove can only be called once after each call to next.
// any other calls must result in an exception.
if (!deleteOk) {
throw new IllegalStateException();
}
howMany--;
deleteOk = false;
}
}
}
There is no reason to restrict the implementation to only work with strings.
public class Repeater<T> implements Iterable<T> {
private T what;
private int howMany;
public Repeater(T what, int howMany) {
this.what = what;
this.howMany = howMany;
}
@Override
public Iterator<T> iterator() {
return new RepeatIterator();
}
// Notice that RepeatIterator doesn't need its own type parameter.
// We would never need to specify its type independently from
// the type provided to Repeater.
private class RepeatIterator implements Iterator<T> {
int remaining = howMany;
boolean deleteOk = false;
@Override
public boolean hasNext() {
return remaining > 0;
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
remaining--;
deleteOk = true;
return what;
}
@Override
public void remove() {
if (!deleteOk) {
throw new IllegalStateException();
}
howMany--;
deleteOk = false;
}
}
}
Anonymous inner classes are often used in cases where we have a single-use class. This is generally the case for Iterator classes:
public class Repeater<T> implements Iterable<T> {
private T what;
private int howMany;
public Repeater(T what, int howMany) {
this.what = what;
this.howMany = howMany;
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() { // Body of the class goes here. It has no name.
int remaining = howMany; // Anonymous classes can't have constructors.
boolean deleteOk = false;
@Override
public boolean hasNext() {
return remaining > 0;
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
remaining--;
deleteOk = true;
return what;
}
@Override
public void remove() {
if (!deleteOk) {
throw new IllegalStateException();
}
howMany--;
deleteOk = false;
}
};
}
}
Type parameters are usually provided when an object is instantiated. When static methods are called, there is no object, so alternative syntax is required:
public class GenericsDemo {
public static <E> void copy(ArrayList<E> from, ArrayList<E> to) {
for (E element : from) {
to.add(element);
}
}
public static void main(String[] args) {
ArrayList<String> a = new ArrayList<>();
ArrayList<String> b = new ArrayList<>();
ArrayList<Integer> c = new ArrayList<>();
//...
GenericsDemo.<String>copy(a, b); // OK!
GenericsDemo.copy(a, b); // OK! (type will be inferred)
copy(a, c); // Will not compile.
}
}