Skip to content

Instantly share code, notes, and snippets.

@btforsythe
Created December 27, 2016 18:55
Show Gist options
  • Save btforsythe/10292b7128ddfc4b734eac1a53e62daf to your computer and use it in GitHub Desktop.
Save btforsythe/10292b7128ddfc4b734eac1a53e62daf to your computer and use it in GitHub Desktop.
nested classes example code
package org.wecancodeit.examples.nestedClasses;
import java.util.Arrays;
import java.util.Iterator;
public class ArrayIterableFactory {
public static class StaticNestedIterable implements Iterable<String> {
private String[] elements;
/**
* We must pass elements into the constructor, since this class is
* static and doesn't have access to
* {@link ArrayIterableFactory#elementsToIterate}.
*/
public StaticNestedIterable(String... elements) {
this.elements = elements;
}
@Override
public Iterator<String> iterator() {
return Arrays.asList(elements).iterator();
}
}
private String[] elementsToIterate;
public ArrayIterableFactory(String... elements) {
this.elementsToIterate = elements;
}
/**
* Though it's not usually done, we could also create an
* {@link InnerIterable} like this if its visibility was changed to
* {@code public}:
*
* <pre>
* {@code
* ArrayIteratorFactory factory = new ArrayIteratorFactory("fee", "fie", "foe");
* Iterable<String> iterable = factory.new InnerIterable();
* }
* </pre>
*
* @return
*/
public Iterable<String> createInnerIterable() {
return new InnerIterable();
}
/**
* Inner (non-static nested) classes are associated with an instance, so
* {@link InnerIterable} can access
* {@link ArrayIterableFactory#elementsToIterate}.
*/
private class InnerIterable implements Iterable<String> {
@Override
public Iterator<String> iterator() {
return Arrays.asList(elementsToIterate).iterator();
}
}
public Iterable<String> createLocalIterable() {
/**
* Local classes are created within a method and have the same scope as
* that method, so this class can access
* {@link ArrayIterableFactory#elementsToIterate}.
*/
class LocalIterable implements Iterable<String> {
@Override
public Iterator<String> iterator() {
return Arrays.asList(elementsToIterate).iterator();
}
}
return new LocalIterable();
}
/**
* This anonymous class instance is created inside an instance method, so it
* has access to {@link ArrayIterableFactory#elementsToIterate}.
*
* @return
*/
public Iterable<String> createAnonymousIterable() {
return new Iterable<String>() {
@Override
public Iterator<String> iterator() {
return Arrays.asList(elementsToIterate).iterator();
}
};
}
/**
* Note that this is a class ({@code static}) method, so doesn't have access to
* {@link ArrayIterableFactory#elementsToIterate}.
*/
public static Iterable<String> createAnonymousIterableStatically(String... elements) {
return new Iterable<String>() {
@Override
public Iterator<String> iterator() {
return Arrays.asList(elements).iterator();
}
};
}
}
package org.wecancodeit.examples.nestedClasses;
import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.assertThat;
import org.junit.Test;
/**
* <p>
* For this demo we'll use {@link Iterable}, since it is a simple interface. Run
* this as you would any other test.
* </p>
*
* <p>
* When should you use nested classes? Definitely not before you are comfortable
* with them, since you can almost always use a top-level class instead.
* Probably not often after you are comfortable with them, since they can be a
* smell indicating that your code is too complicated. One place they shine is
* within tests -- if we are using interfaces extensively, tests will often
* create inner or anonymous classes to manually fake a collaborator, avoiding
* using a real object.
* </p>
*/
public class ArrayIterableFactoryDemo {
/**
* Nested classes that are static are called static nested classes. They
* only have access to class (static) fields.
*
* Note that static nested classes are associated with their containing
* class rather than an instance of their containing class, so we do not
* need to (and should not) create an instance of
* {@link ArrayIterableFactory}.
*/
@Test
public void shouldCreateStaticNestedClass() {
Iterable<String> iterable = new ArrayIterableFactory.StaticNestedIterable("foo", "bar", "baz");
assertThat(iterable, contains("foo", "bar", "baz"));
}
/**
* Nested classes that are not static are called inner classes. They are
* associated with an instance (object) and have access to instance fields
* as well as static fields.
*/
@Test
public void shouldCreateInnerClass() {
ArrayIterableFactory factory = new ArrayIterableFactory("foo", "bar", "baz");
Iterable<String> iterable = factory.createInnerIterable();
assertThat(iterable, contains("foo", "bar", "baz"));
}
/**
* Local classes are almost never seen. They are classes created and used
* inside a method, only existing for the scope of that method.
*
* Local classes have access to the same scope as the method that
* creates them.
*/
@Test
public void shouldCreateLocalClass() {
ArrayIterableFactory factory = new ArrayIterableFactory("foo", "bar", "baz");
Iterable<String> iterable = factory.createLocalIterable();
assertThat(iterable, contains("foo", "bar", "baz"));
}
/**
* Anonymous classes are relatively common, giving us an easy way to create
* a specific implementation when we're not going to be reusing it later.
* They are essentially single-use class definitions.
*
* Anonymous classes have access to the same scope as the method that
* creates them.
*/
@Test
public void shouldCreateAnonymousClassViaInstanceMethod() {
ArrayIterableFactory factory = new ArrayIterableFactory("foo", "bar", "baz");
Iterable<String> iterable = factory.createAnonymousIterable();
assertThat(iterable, contains("foo", "bar", "baz"));
}
@Test
public void shouldCreateAnonymousClassViaClassMethod() {
Iterable<String> iterable = ArrayIterableFactory.createAnonymousIterableStatically("foo", "bar", "baz");
assertThat(iterable, contains("foo", "bar", "baz"));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment