Voorbeeld van een innerlijke klasse

De klasse AbstractList heeft een interne klasse Itr . Het is een implementatie van de Iterator- interface, die het mogelijk maakt om elementen van collecties één voor één op te halen:

private class Itr implements Iterator<E> {
	int cursor = 0;
	int lastRet = -1;
	int expectedModCount = modCount;

	public boolean hasNext() {
    		return cursor != size();
	}

	public E next() {
    	checkForComodification();
    	try {
        	int i = cursor;
        	E next = get(i);
        	lastRet = i;
        	cursor = i + 1;
        	return next;
    	} catch (IndexOutOfBoundsException e) {
        	checkForComodification();
        	throw new NoSuchElementException(e);
    	}
	}

	public void remove() {
    	if (lastRet < 0)
        	throw new IllegalStateException();
    	checkForComodification();

    	try {
        	AbstractList.this.remove(lastRet);
        	if (lastRet < cursor)
            	cursor--;
        	lastRet = -1;
        	expectedModCount = modCount;
    	} catch (IndexOutOfBoundsException e) {
   	     throw new ConcurrentModificationException();
    	}
	}

	final void checkForComodification() {
    	if (modCount != expectedModCount)
        	throw new ConcurrentModificationException();
	}
}

Het wordt gebruikt in de iteratormethode :

public Iterator<E> iterator() {
	return new Itr();
}

Zo krijgt elke afstammeling van AbstractList een kant-en-klare iterator. En als u de iterator moet aanpassen, kunt u uw eigen klasse implementeren die Iterator of Itr erft en vervolgens de iterator -methode overschrijven. Dit is bijvoorbeeld wat de klasse ArrayList doet.

De Itr- klasse is niet-statisch. Als resultaat heeft het Itr- object een verwijzing naar de instantie AbstractList en heeft het toegang tot zijn methoden ( size , get , remove ).

Voorbeeld van een statische geneste klasse

De klasse Integer heeft een geneste klasse IntegerCache .

private static class IntegerCache {
	static final int low = -128;
	static final int high;
	static final Integer[] cache;
	static Integer[] archivedCache;

	static {
    	int h = 127;
    	String integerCacheHighPropValue =
        	VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
    	if (integerCacheHighPropValue != null) {
        	try {
            	h = Math.max(parseInt(integerCacheHighPropValue), 127);
            	h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
        	} catch( NumberFormatException nfe) {
        	}
    	}
    	high = h;

    	VM.initializeFromArchive(IntegerCache.class);
    	int size = (high - low) + 1;

    	if (archivedCache == null || size > archivedCache.length) {
        	Integer[] c = new Integer[size];
        	int j = low;
        	for(int i = 0; i < c.length; i++) {
            	c[i] = new Integer(j++);
        	}
        	archivedCache = c;
    	}
    	cache = archivedCache;
    	assert IntegerCache.high >= 127;
}

	private IntegerCache() {}
}

IntegerCache bevat functionaliteit die een cache maakt en de cachebereiken en de waarden in de cache zelf opslaat. Alles wat met de cache te maken heeft, wordt dus in een aparte klasse bewaard. Dit maakt het gemakkelijker om de code te lezen en aan te passen. Code die de klasse gebruikt:

public static Integer valueOf(int i) {
	if (i >= IntegerCache.low && i <= IntegerCache.high)
    		return IntegerCache.cache[i + (-IntegerCache.low)];
	return new Integer(i);
}

De klasse IntegerCache heeft geen toegang tot niet-statische velden en methoden van de klasse Integer . Bovendien is het alleen toegankelijk via de statische valueOf- methode. Dat wil zeggen, het is gebonden aan de Integer- klasse zelf, niet aan zijn individuele instanties. En dat betekent dat IntegerCache statisch is.

Voorbeeld van een anonieme innerlijke klasse

Laten we als voorbeeld van een anonieme klasse de InputStream en zijn statische nullInputStream -methode nemen:

public static InputStream nullInputStream() {
    return new InputStream() {
    	private volatile boolean closed;

    	private void ensureOpen() throws IOException {
        	if (closed) {
            		throw new IOException("Stream closed");
        	}
    	}

    	@Override
    	public int available () throws IOException {
        	ensureOpen();
        	return 0;
    	}

    	@Override
    	public int read() throws IOException {
        	ensureOpen();
        	return -1;
    	}

    	@Override
    	public int read(byte[] b, int off, int len) throws IOException {
        	Objects.checkFromIndexSize(off, len, b.length);
        	if (len == 0) {
            		return 0;
        	}
        	ensureOpen();
        	return -1;
    	}

    	@Override
    	public byte[] readAllBytes() throws IOException {
        	ensureOpen();
        	return new byte[0];
    	}

    	@Override
    	public int readNBytes(byte[] b, int off, int len)throws IOException {
        	Objects.checkFromIndexSize(off, len, b.length);
        	ensureOpen();
        	return 0;
    	}

    	@Override
   	 public byte[] readNBytes(int len) throws IOException {
        	if (len < 0) {
            		throw new IllegalArgumentException("len < 0");
        	}
        	ensureOpen();
        	return new byte[0];
    	}

    	@Override
    	public long skip(long n) throws IOException {
        	ensureOpen();
        	return 0L;
    	}

    	@Override
    	public void skipNBytes(long n) throws IOException {
        	ensureOpen();
        	if (n > 0) {
            		throw new EOFException();
        	}
    	}

    	@Override
    	public long transferTo(OutputStream out) throws IOException {
        	Objects.requireNonNull(out);
        	ensureOpen();
        	return 0L;
    	}

    	@Override
    	public void close() throws IOException {
        	closed = true;
    	}
    };
}

De methode retourneert een lege InputStream , geïmplementeerd door een anonieme klasse. Omdat het niet de bedoeling is dat de klas nakomelingen heeft, hebben we deze anoniem gemaakt.

Met de toevoeging van de Java Stream API zijn anonieme klassen alomtegenwoordig geworden: alle lambda-expressies zijn anonieme klassen die een functionele interface implementeren. Overweeg enkele voorbeelden.

De klasse AbstractStringBuilder bevat de ouder van de beroemde klassen StringBuilder en StringBuffer :

@Override
public IntStream chars() {
	return StreamSupport.intStream(
        	() -> {
            	byte[] val = this.value;
            	int count = this.count;
            	byte coder = this.coder;
            	return coder == LATIN1
                   	? new StringLatin1.CharsSpliterator(val, 0, count, 0)
                   	: new StringUTF16.CharsSpliterator(val, 0, count, 0);
        	},
        	Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED,
        	false);
}

De klasse Files heeft een functie voor het converteren van een Closeable naar een Runnable :

private static Runnable asUncheckedRunnable(Closeable c) {
	return () -> {
    	try {
        	c.close();
    	} catch (IOException e) {
        	throw new UncheckedIOException(e);
    	}
	};
}

De klasse Class heeft een methode om een ​​stringrepresentatie van een methode te verkrijgen:

private String methodToString(String name, Class<?>[] argTypes) {
	return getName() + '.' + name +
        	((argTypes == null || argTypes.length == 0) ?
        	"()" :
        	Arrays.stream(argTypes)
        	        .map(c -> c == null ? "null" : c.getName())
                	.collect(Collectors.joining(",", "(", ")")));
}