Last active
August 29, 2015 14:24
-
-
Save esmasui/d7854840597ad6cc2707 to your computer and use it in GitHub Desktop.
NullObjectぱてーん
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 com.example; | |
import java.lang.reflect.Array; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Collection; | |
import java.util.Collections; | |
import java.util.Iterator; | |
import java.util.List; | |
public abstract class CollectionUtils { | |
private CollectionUtils() { | |
throw new UnsupportedOperationException("No instances"); | |
} | |
public static int[] intCollectionToPrimitiveArray(Collection<Integer> collection) { | |
final Iterator<Integer> iterator = collection.iterator(); | |
final int[] primitives = new int[collection.size()]; | |
for (int i = 0, length = primitives.length; i < length; ++i) { | |
primitives[i] = iterator.next(); | |
} | |
return primitives; | |
} | |
public static long[] longCollectionToPrimitiveArray(Collection<Long> collection) { | |
final Iterator<Long> iterator = collection.iterator(); | |
final long[] primitives = new long[collection.size()]; | |
for (int i = 0, length = primitives.length; i < length; ++i) { | |
primitives[i] = iterator.next(); | |
} | |
return primitives; | |
} | |
public static <S, T> T[] copyToDestinationTypeArray(S[] src, Class<T> destinationType) { | |
final T[] arr = newArrayInstance(destinationType, src.length); | |
System.arraycopy(src, 0, arr, 0, src.length); | |
return arr; | |
} | |
public static <T> T[] nullToEmptyArray(T[] array, Class<?> arrayType) { | |
return array == null ? (T[]) Array.newInstance(arrayType, 0) : array; | |
} | |
public static <T> List<T> nullToEmptyList(T... array) { | |
return array == null ? Collections.<T>emptyList() : Arrays.asList(array); | |
} | |
public static <T> List<T> nullToEmptyList(List<T> list) { | |
return list == null ? Collections.<T>emptyList() : list; | |
} | |
public static <T> T[] objectsToArray(T... array) { | |
return array; | |
} | |
public static <T> T[] collectionToArray(Collection<T> collection, Class<T> destinationType) { | |
return collection.toArray(newArrayInstance(destinationType, collection.size())); | |
} | |
public static <T> List<T> arrayToList(T[] array) { | |
final ArrayList<T> list = new ArrayList<>(array.length); | |
for (T each : array) { | |
list.add(each); | |
} | |
return list; | |
} | |
public static <T> Collection<T> arrayToCollection(T[] array) { | |
return arrayToList(array); | |
} | |
public static <T> T[] concat(T[] a, T... b) { | |
final T[] arr = (T[]) newArrayInstance(a.getClass().getComponentType(), a.length + b.length); | |
System.arraycopy(a, 0, arr, 0, a.length); | |
System.arraycopy(b, 0, arr, a.length, b.length); | |
return arr; | |
} | |
private static <T> T[] newArrayInstance(Class<T> type, int length) { | |
return (T[]) Array.newInstance(type, length); | |
} | |
public static <T> int indexOf(T[] values, T value) { | |
for (int i = 0; i < values.length; i++) { | |
if (values[i].equals(value)) { | |
return i; | |
} | |
} | |
return -1; | |
} | |
} |
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 com.example; | |
import java.lang.reflect.Array; | |
import java.lang.reflect.InvocationHandler; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Proxy; | |
import java.util.Collections; | |
import java.util.HashMap; | |
import java.util.LinkedHashSet; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import static com.example.CollectionUtils.concat; | |
import static com.example.CollectionUtils.nullToEmptyArray; | |
import static com.example.CollectionUtils.objectsToArray; | |
/** | |
* Null objectを生成するためのファクトリーメソッドを提供するクラス | |
*/ | |
public abstract class NullObjects { | |
public interface Producer { | |
Object produce(Class<?> returnType); | |
} | |
private NullObjects() { | |
throw new UnsupportedOperationException("No instances"); | |
} | |
public static Builder builder() { | |
return new Builder(); | |
} | |
public static Builder defaults() { | |
return builder().defaultValue().emptyCollection(); | |
} | |
public static class Builder { | |
private final Set<Producer> producers = new LinkedHashSet<>(); | |
/** | |
* Producer を追加する。優先順位は追加された順に依る | |
* | |
* @param producer | |
* @return このビルダーオブジェクト | |
*/ | |
public Builder produce(Producer producer) { | |
producers.add(producer); | |
return this; | |
} | |
/** | |
* プリミティブ型の返り値のメソッドではデフォルト値(<a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">Primitive Data Types</a>)を返すようにNull objectを構成する。 | |
* | |
* @return このビルダーオブジェクト | |
*/ | |
public Builder defaultValue() { | |
return produce(DEFAULT_VALUE_PRODUCER); | |
} | |
/** | |
* 配列、List、Set、Map型の返り値のメソッドで、それぞれ要素数0の値を返すようにNull objectを構成する。 | |
* | |
* @return このビルダーオブジェクト | |
*/ | |
public Builder emptyCollection() { | |
return produce(EMPTY_COLLECTION_PRODUCER); | |
} | |
public <T> T build(Class<T> type, Class<?>... otherTypes) { | |
return (T) Proxy.newProxyInstance(type.getClassLoader(), concat(objectsToArray(type), nullToEmptyArray(otherTypes, Class.class)), new NullObjectsInvocationHandler(producers.toArray(new Producer[producers.size()]))); | |
} | |
} | |
private static class NullObjectsInvocationHandler implements InvocationHandler { | |
private final Producer[] producers; | |
private NullObjectsInvocationHandler(Producer... producers) { | |
this.producers = producers; | |
} | |
@Override | |
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |
final Class<?> returnType = method.getReturnType(); | |
for (Producer each : producers) { | |
final Object returnValue = each.produce(returnType); | |
if (returnValue != null) { | |
return returnValue; | |
} | |
} | |
return null; | |
} | |
} | |
private static final Producer EMPTY_COLLECTION_PRODUCER = new Producer() { | |
@Override | |
public Object produce(Class<?> returnType) { | |
if (returnType.isArray()) { | |
return Array.newInstance(returnType.getComponentType(), 0); | |
} | |
if (returnType.equals(List.class)) { | |
return Collections.emptyList(); | |
} | |
if (returnType.equals(Set.class)) { | |
return Collections.emptySet(); | |
} | |
if (returnType.equals(Map.class)) { | |
return Collections.emptyMap(); | |
} | |
return null; | |
} | |
}; | |
private static final Producer DEFAULT_VALUE_PRODUCER = new Producer() { | |
private Map<Class<?>, Object> primitives = new HashMap<>(); | |
{ | |
primitives.put(byte.class, Byte.valueOf((byte) 0)); | |
primitives.put(short.class, Short.valueOf((short) 0)); | |
primitives.put(int.class, Integer.valueOf(0)); | |
primitives.put(long.class, Long.valueOf(0L)); | |
primitives.put(float.class, Float.valueOf(0f)); | |
primitives.put(double.class, Double.valueOf(0)); | |
primitives.put(char.class, Character.valueOf((char) 0)); | |
primitives.put(boolean.class, Boolean.valueOf(false)); | |
} | |
@Override | |
public Object produce(Class<?> returnType) { | |
return primitives.get(returnType); | |
} | |
}; | |
} |
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
ProgressListener EMPTY_PROGRESS_LISTENER = NullObjects.defaults().build(ProgressListener.class); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment