Created
December 27, 2016 18:55
-
-
Save btforsythe/10292b7128ddfc4b734eac1a53e62daf to your computer and use it in GitHub Desktop.
nested classes example code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
}; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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