public class ObjectToProxy
{
List<ObjectToProxy> potentiallyCircularReference;
}
public class SubClass
{
private ObjectToProxy aField;
Set<ObjectToProxy> aSetOfObjectsToProxy;
}
public class CrazyObject
{
Map<Integer, ObjectToProxy> proxiedObjects;
List<SubClass> manySubClasses;
}
public class ComplexObject
{
List<CrazyObject> crazyObjects;
private final ObjectToProxy storedAsAField;
}
I have a complex object graph. Lets say it looks a little like the one above (even though it is much deeper in the real system). I would like, after being given ComplexObject, to be able to traverse the object graph and replace all ObjectToProxys with a proxying object.
Is this doable?
The reason for this is that we have some pretty big nasty objects which we partially load on the servers side (legacy, you're my friend!). We have a semi-working solution that uses proxying on the client side to go through and loads the full object when needed.
edit I would like to replace every instance of ObjectProxy connected to a ComplexObject.
public static class ProxyObject extends ObjectToProxy
{
private final ObjectToProxy objectToProxy;
public ProxyObject(ObjectToProxy objectToProxy)
{
this.objectToProxy = objectToProxy;
}
#Override
public String toString()
{
return "ProxyObject";
}
}
public static class ObjectToProxy
{
List<ObjectToProxy> potentiallyCircularReference;
public ObjectToProxy()
{
potentiallyCircularReference = new ArrayList<>();
potentiallyCircularReference.add(this);
}
#Override
public String toString()
{
return "ObjectToProxy";
}
}
public static class SubClass
{
ObjectToProxy aField;
Set<ObjectToProxy> aSetOfObjectsToProxy;
}
public static class CrazyObject
{
Map<Integer, ObjectToProxy> proxiedObjects;
List<SubClass> manySubClasses;
public CrazyObject()
{
proxiedObjects = new HashMap<>();
proxiedObjects.put(1, new ObjectToProxy());
}
}
public static class ComplexObject
{
List<CrazyObject> crazyObjects;
final ObjectToProxy storedAsAField;
public ComplexObject()
{
this.storedAsAField = new ObjectToProxy();
crazyObjects = new ArrayList<>();
crazyObjects.add(new CrazyObject());
}
#Override
public String toString()
{
return "myField: " + storedAsAField.toString();
}
}
public static void main(String[] args) throws Exception
{
ComplexObject obj = new ComplexObject();
Set<Object> visitedObjects = Sets.newIdentityHashSet();
Queue<Object> objectsToVisit = new LinkedList<>();
visitedObjects.add(obj);
objectsToVisit.add(obj);
while (!objectsToVisit.isEmpty())
{
handleFields(objectsToVisit.poll(), visitedObjects, objectsToVisit);
}
System.out.println(obj.toString());
}
private static void handleFields(Object obj, Set<Object> visitedObjects, Queue<Object> objectsToVisit) throws Exception
{
List<Field> fields = getAllFields(obj);
for (Field field : fields)
{
field.setAccessible(true);
Object fieldValue = field.get(obj);
if (fieldValue != null && !visitedObjects.contains(fieldValue))
{
if (fieldValue instanceof Object[])
{
visitedObjects.add(fieldValue);
Object[] array = (Object[])fieldValue;
for (Object arrayObj : array)
{
if (arrayObj != null && !objectsToVisit.contains(arrayObj))
{
visitedObjects.add(arrayObj);
if (!DontLookAt.contains(arrayObj.getClass()))
objectsToVisit.add(arrayObj);
}
}
}
else
{
if (!DontLookAt.contains(fieldValue.getClass()))
objectsToVisit.add(fieldValue);
}
if (fieldValue.getClass().equals(ObjectToProxy.class))
{
field.set(obj, new ProxyObject((ObjectToProxy)fieldValue));
}
else if (fieldValue instanceof ObjectToProxy[])
{
ObjectToProxy[] array = (ObjectToProxy[])fieldValue;
for (int i = 0; i < array.length; i++)
{
if (array[i] != null)
array[i] = new ProxyObject(array[i]);
}
}
}
}
}
private static final Set<Class> DontLookAt = getDontLookAtSet();
private static Set<Class> getDontLookAtSet()
{
Set<Class> set = new HashSet<>();
set.add(Long.class);
set.add(Boolean.class);
set.add(Integer.class);
set.add(String.class);
set.add(Byte.class);
set.add(Double.class);
set.add(Float.class);
set.add(Class.class);
return set;
}
private static List<Field> getAllFields(Object obj) throws Exception
{
List<Field> output = new ArrayList<>();
Class klazz = obj.getClass();
while (!klazz.equals(Object.class))
{
Field[] fields = klazz.getDeclaredFields();
output.addAll(Arrays.asList(fields));
klazz = klazz.getSuperclass();
}
return output;
}
For anyone wondering, The above simulates and does what I'm after. I'm sure it isn't perfect, but it is good enough for my purposes.
Related
private static final Map<Integer, GameObject> OBJECT = new ConcurrentHashMap<>();
I have a map in which I store GameObjects, which is extended by PlayerObject, NpcObject, ItemObject.
I'm trying to create a method on which I call the object by ID and class type and cast at it directly and if it's not exists or the class of the object ID does not match the given one to return null.
So for example
final PlayerObject object = getObject(<id>, PlayerObject);
Is there any way?
Edit:
I managed to do this:
public <T extends EventObject> T getObject(final int objectId)
{
final EventObject object = OBJECT.get(objectId);
return Objects.nonNull(object) && object.getClass() == ? T (object) : null;
}
But i don't want to use Class<? extends EventObject> in parameter of this method. Can't i somehow check using the generic T if it's the same class to cast it and return or else null?
You can use Class#isInstance to check if the object's type is correct and Class#cast to convert the object to the correct type.
public static <T extends GameObject> T getObject(Integer id, Class<T> clazz) {
GameObject obj = OBJECT.get(id);
if(!clazz.isInstance(obj)) return null;
return clazz.cast(obj);
}
// ...
final PlayerObject object = getObject(<id>, PlayerObject.class);
try this complete generic method:
public static <T> T getObject(int id, Class<T> c){
Object object = OBJECT.get(id);
return object != null && object.getClass() == c ? c.cast(object) : null;
}
The other parts of the program:
private static final Map<Integer, GameObject> OBJECT = new ConcurrentHashMap<>();
public static void main(String[] args) throws ParseException {
init();
PlayerObject p = getObject(3, PlayerObject.class);
ItemObject i = getObject(3, ItemObject.class);
PlayerObject p2 = getObject(4, PlayerObject.class);
System.out.println(p);
System.out.println(i);
System.out.println(p2);
}
private static void init() {
OBJECT.put(1, new PlayerObject(1, "SomePlayer1"));
OBJECT.put(2, new PlayerObject(2, "SomePlayer2"));
OBJECT.put(3, new ItemObject(3, 5));
OBJECT.put(4, new ItemObject(4, 7));
}
GameObject.class
public class GameObject {
protected int id;
GameObject(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
}
PlayerObject.class
public class PlayerObject extends GameObject {
private String playerName;
PlayerObject(int id, String playerName) {
super(id);
this.playerName = playerName;
}
public String getPlayerName() {
return this.playerName;
}
public void setPlayerName(String playerName) {
this.playerName = playerName;
}
#Override
public String toString() {
return "PlayerObject{\"id\": " +
this.id +
", \"playerName\": \"" +
this.playerName +
"\"}";
}
}
ItemObject.class
public class ItemObject extends GameObject {
private int itemCount;
ItemObject(int id, int itemCount) {
super(id);
this.itemCount = itemCount;
}
public int getItemCount() {
return this.itemCount;
}
public void setItemCount(int itemCount) {
this.itemCount = itemCount;
}
#Override
public String toString() {
return "ItemObject{\"id\": " +
this.id +
", \"itemCount\": " +
this.itemCount +
"}";
}
}
And the output of the program:
PlayerObject{"id": 1, "playerName": "SomePlayer1"}
ItemObject{"id": 3, "itemCount": 5}
null
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
public class Stackoverflow_68734414 {
private static Map<Integer, GameObject> OBJECT_MAP = new ConcurrentHashMap<>();
public static void main(String[] args) {
PlayerObject po = new PlayerObject();
ItemObject io = new ItemObject();
OBJECT_MAP.put(1, po);
OBJECT_MAP.put(2, io);
PlayerObject p1 = getObject(1, PlayerObject.class);
PlayerObject p2 = getObject(2, PlayerObject.class);
ItemObject i1 = getObject(1, ItemObject.class);
ItemObject i2 = getObject(2, ItemObject.class);
System.out.println(p1);
System.out.println(p2);
System.out.println(i1);
System.out.println(i2);
}
public static <T extends GameObject> T getObject(Integer id, Class<T> klass){
GameObject object = OBJECT_MAP.get(id);
if(Objects.nonNull(object) && (object.getClass() == klass)) {
return klass.cast(object);
} else{
return null;
}
}
}
class GameObject{
}
class PlayerObject extends GameObject {
}
class ItemObject extends GameObject{
}
Output is as expected:
PlayerObject#179d3b25
null
null
ItemObject#254989ff
Are you looking for something similar to this:
public boolean isItemObject(int id){
GameObject obj = OBJECT.get(id)
if(obj instanceof ItemObject && obj != null){
return true;
}
return false;
}
public boolean isPlayerObject(int id){
GameObject obj = OBJECT.get(id)
if(obj instanceof PlayerObject && obj != null){
return true;
}
return false;
}
public boolean isNPCObject(int id){
GameObject obj = OBJECT.get(id)
if(obj instanceof NpcObject && obj != null){
return true;
}
return false;
}
//...
final PlayerObject pObject = isPlayerObject(objectID) ? OBJECT.get(id) : null;
Example I have data layer after
public class DemoData implements Cloneable {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); //To change body of generated methods, choose Tools | Templates.
}
}
I want to assign data values (DemoData) to a duplicate data (DemoData clone) layer as follows
public static void main(String[] args) {
DemoData demoData = new DemoData();
demoData.setName("Class Sources");
testReflectionDemo(demoData);
}
private static DemoData testReflectionDemo(DemoData demoData) {
try {
DemoData clone = (DemoData) demoData.clone();
clone.setName(demoData.getName());
clone.setValue(demoData.getValue());
return clone;
} catch (CloneNotSupportedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
I want to convert the method testReflectionDemo(DemoData demoData) to method testReflectionDemo(T t) reflection as shown below.I do not know how to continue, please help me
public <T> T testReflectionDemo(T t){
Class<?> aClass = t.getClass();
for (Method method : aClass.getMethods()) {
}
return null;
}
Thank you all for the help for my question,I've removed the clone method, I just applied reflection.Hi #dabaicai.Your code helped me with the idea,I thought passing the value to the private field would be easier a little.
public static <T> T clazzClone(T t) throws InstantiationException, IllegalAccessException, NoSuchFieldException {
Class<?> clazzRoot = t.getClass();
Object newInstance = clazzRoot.newInstance();
Field[] fieldsClone = newInstance.getClass().getDeclaredFields();
for (Field fieldClone : fieldsClone) {
fieldClone.setAccessible(true);
fieldClone.set(newInstance, getContent(t, fieldClone.getName()));
}
return (T) newInstance;
}
private static String getContent(Object aClass, String name) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Field declaredField = aClass.getClass().getDeclaredField(name);
declaredField.setAccessible(true);
return (String) declaredField.get(aClass);
}
My program means when I need to edit user input data to output the results I want,with a common filter function
fieldClone.set(newInstance,methodYourEdit(getContent(t, fieldClone.getName())));
If the argument of testReflectionDemo is a javabean,it means that the class of argument have several a pair method of setXXX and 'getXXX,and thegetXXXdon't have argument,thesetXXX` just have one argument.If is this,the following code can copy the property from old object to new object.
Class<?> aClass = t.getClass();
Object result = aClass.newInstance();
Map<String,MethodHolder> map=new HashMap<>();
for (Method method : aClass.getMethods()) {
if(method.getName().startsWith("get") && method.getParameterTypes().length==0){
String property=method.getName().substring(3);
MethodHolder hodler = map.get(property);
if(hodler ==null){
map.put(property, new MethodHolder(property, method, null));
continue;
}
hodler.getMethod=method;
}else if (method.getName().startsWith("set") && method.getParameterTypes().length==1) {
String property=method.getName().substring(3);
MethodHolder holder = map.get(property);
if(holder ==null){
map.put(property, new MethodHolder(property, null, method));
continue;
}
holder.setMethod=method;
}
}
List<MethodHolder> collect = map.values().stream().filter(item -> item.setMethod != null && item.getMethod != null).collect(Collectors.toList());
for (MethodHolder holder : collect) {
Object property = holder.getMethod.invoke(t);
holder.setMethod.invoke(result,property);
}
return (T)result;
The MethodHolder just have some field:
public static class MethodHolder{
private String property;
private Method getMethod;
private Method setMethod;
public MethodHolder() {
}
public MethodHolder(String property, Method getMethod, Method setMethod) {
this.property = property;
this.getMethod = getMethod;
this.setMethod = setMethod;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MethodHolder)) return false;
MethodHolder that = (MethodHolder) o;
return Objects.equals(property, that.property);
}
#Override
public int hashCode() {
return Objects.hash(property);
}
}
Pay attention of that the following code just make shallow copy.
I often have to deal with DTOs that contains other DTOs and I'd like to scan one object's attributes (and their own attributes, recursively) and retrieve every accessible object of class Bingo in the whole hierarchy.
For example, when I have the following :
public static class Bingo {
// the one I want to get
}
public static class Foo {
private Bar bar;
private Bingo bingo;
private List<Bingo> bingos;
// getters & setters
}
public static class Bar {
private Bingo bingo;
// getters & setters
}
I'd like to get all instances of Bingo found in attributes of my Foo object, including the ones in the Bar object and the List.
Is there a library conveniently doing that ?
A more complete test case (using a bit of JUnit) :
public static class Bingo {
private final int id;
public Bingo(int in_id) {
id = in_id;
}
#Override
public String toString() {
return "Bingo#"+String.valueOf(id);
}
}
public static class BingoWrapper {
private Bingo bingo;
public Bingo getBingo() {
return bingo;
}
public void setBingo(Bingo in_bingo) {
bingo = in_bingo;
}
}
public static class BingoFactory {
private final List<Bingo> ALL_BINGOS = new ArrayList<>();
private int sequence = 0;
public Bingo createBingo(){
Bingo l_bingo = new Bingo(sequence++);
ALL_BINGOS.add(l_bingo);
return l_bingo;
}
public BingoWrapper createBingoWrapper(){
BingoWrapper l_bar = new BingoWrapper();
l_bar.setBingo(createBingo());
return l_bar;
}
public List<Bingo> getAllBingos(){
return ALL_BINGOS.stream().collect(Collectors.toList());
}
}
public static class Foo {
private Bingo bingo;
private BingoWrapper wrapper;
private Bingo[] array;
private Collection<Object> collection;
private Map<Object,Object> map;
public Bingo getBingo() {
return bingo;
}
public void setBingo(Bingo in_bingo) {
bingo = in_bingo;
}
public BingoWrapper getWrapper() {
return wrapper;
}
public void setWrapper(BingoWrapper in_bar) {
wrapper = in_bar;
}
public Bingo[] getArray() {
return array;
}
public void setArray(Bingo[] in_array) {
array = in_array;
}
public Collection<Object> getCollection() {
return collection;
}
public void setCollection(Collection<Object> in_collection) {
collection = in_collection;
}
public Map<Object, Object> getMap() {
return map;
}
public void setMap(Map<Object, Object> in_map) {
map = in_map;
}
}
#Test
public void test(){
BingoFactory l_bingoFactory = new BingoFactory();
Foo l_foo = new Foo();
l_foo.setBingo(l_bingoFactory.createBingo()); // one in a field
l_foo.setWrapper(l_bingoFactory.createBingoWrapper()); // one in a field of a field
l_foo.setArray(new Bingo[]{l_bingoFactory.createBingo()}); // one in an array in a field
l_foo.setCollection(Arrays.asList(
l_bingoFactory.createBingo(), // one in Collection in a field
l_bingoFactory.createBingoWrapper())); // one in a field of an item in a Collection in a field
Map<Object,Object> l_map = new HashMap<>();
l_foo.setMap(l_map);
l_map.put("key", l_bingoFactory.createBingo()); // one as a key in a Map in a field
l_map.put(l_bingoFactory.createBingo(), "value"); // one as a value in a Map in a field
l_map.put("keyAgain", l_bingoFactory.createBingoWrapper()); // one wrapped in a value in a Map in a Field
l_map.put(l_bingoFactory.createBingoWrapper(), "valueAgain"); // one wrapped in a key in a Map in a field
List<Bingo> l_found = BeanUtils.scanObjectForType(l_foo, Bingo.class); // Magic happens here
System.out.println(l_found); // for debug
Assert.assertTrue(l_found.containsAll(l_bingoFactory.getAllBingos())); // I want them ALL
}
A solution with Spring's BeanUtils : (I've added a boolean to decide whereas objects of input class needed to be scanned or not. (i.e. do you expect your Bingo objects to contain other objects of type Bingo ?))
public static <T> List<T> scanObjectForType(Object in_object, Class<T> in_type, boolean in_scanSameType){
return scanObjectForType(in_object, in_type, in_scanSameType, new HashSet<>());
}
private static <T> List<T> scanObjectForType(Object in_object, Class<T> in_type, boolean in_scanSameType, Set<Object> in_alreadyScanned){
if(in_type == null){
throw new IllegalArgumentException("in_type should not be null");
}
if(in_object instanceof Class){
throw new IllegalArgumentException("in_type should not be a Class");
}
if(in_object == null || in_alreadyScanned.contains(in_object)){
return Collections.emptyList();
}
in_alreadyScanned.add(in_object); // to prevent infinite loop when inner object references outer object
if(in_type.isInstance(in_object)){
return Collections.singletonList((T) in_object);
}
List<T> l_result = new ArrayList<>();
if(in_type.isInstance(in_object)){
l_result.add((T) in_object);
if(!in_scanSameType){
return l_result;
}
}
if(in_object instanceof Object[]){
for(Object l_item : (Object[]) in_object){
l_result.addAll(scanObjectForType(l_item, in_type, in_scanSameType, in_alreadyScanned));
}
} else if(in_object instanceof Collection){
for(Object l_item : (Collection<Object>) in_object){
l_result.addAll(scanObjectForType(l_item, in_type, in_scanSameType, in_alreadyScanned));
}
} else if(in_object instanceof Map){
Map<Object,Object> l_map = (Map<Object,Object>) in_object;
for(Map.Entry<Object, Object> l_entry : l_map.entrySet()){
l_result.addAll(scanObjectForType(l_entry.getKey(), in_type, in_scanSameType, in_alreadyScanned));
l_result.addAll(scanObjectForType(l_entry.getValue(), in_type, in_scanSameType, in_alreadyScanned));
}
} else {
PropertyDescriptor[] l_descriptors = org.springframework.beans.BeanUtils.getPropertyDescriptors(in_object.getClass());
for(PropertyDescriptor l_descriptor : l_descriptors){
Method l_readMethod = l_descriptor.getReadMethod();
if(l_readMethod != null){
try {
Object l_readObject = l_readMethod.invoke(in_object);
if(l_readObject != null
&& !l_readObject.equals(in_object) // prevents infinite loops
&& !(l_readObject instanceof Class)){ // prevents weird loops when accessing properties of classes
l_result.addAll(scanObjectForType(l_readObject,in_type, in_scanSameType, in_alreadyScanned));
}
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
// too bad but continue
LOGGER.warn("Got an error trying to access field : ", e);
continue;
}
}
}
}
return l_result;
}
Its limitations :
Only scan properties with public accessors
Does not scan Class types (to prevent scanning of the whole ClassLoader's classes, and because the use-case is DTO-oriented).
Relies on recursivity. I guess it might be prettier to implement a BeanVisitor object that operates on a loop over a Set of nested beans.
Will scan Objects returned by getter methods that may not be properties.
It's not tested with inheritence.
I'm using a Singleton to store a cache of objects but whenever I call the Singleton and add to the HashMap, it has no values.
The cache is checked at the beginning of a method (when the size is 1) but when adding to the HashMap again, the size of it is 0. The size of it therefore alternates between 0 and 1.
public class CachedObjects
{
static HashMap<String, Object> cachedObjects = new HashMap<>();
private static class InstanceHolder
{
private static final CachedObjects instance = new CachedObjects();
}
public static CachedObjects getInstance()
{
return CachedObjects.InstanceHolder.instance;
}
public void addObjectToCache(Object object)
{
cachedObjects.put(object.getTitle(), object);
}
public Object checkCacheForObject(String title)
{
Iterator it = cachedObjects.entrySet().iterator();
while (it.hasNext())
{
Map.Entry pair = (Map.Entry) it.next();
if (pair.getKey().equals(title))
{
return (Object) pair.getValue();
}
it.remove(); // avoids a ConcurrentModificationException
}
return null;
}
}
Where it's called:
public Object getObjectInfoFrom(String title)
{
Object cachedObjectCheck = CachedObjects.getInstance().checkCacheForObject(title);
// Size of HashMap is usually 1 here
if (cachedObjectCheck != null)
{
return cachedObjectCheck ;
}
// Lots of DB fetching here
Object object = new Object(DB details above);
CachedObjects.getInstance().addObjectToCache(object);
// The size of the HashMap always seems to be empty here
return object;
}
public class MyContext {
private static MyContext ourInstance = null;
private HashMap<String, String> translatedValue;
public static MyContext getInstance() {
if (ourInstance == null)
ourInstance = new MyContext();
return ourInstance;
}
private MyContext() {
translatedValue = new HashMap<>();
}
public void addTranslatedValue(String title, String value) {
translatedValue.put(title, value);
}
public String getTranslatedValue(String value) {
return translatedValue.get(value);
}
}
Using
MyContext.getInstance().addTranslatedValue("Next", valueTranslated);
System.out.println(myContext.getTranslatedValue("Next"));
Result
valueTranslated
First of all, this is not singleton because you have not hidden the constructor.
second, you need to remove this line:
it.remove(); // avoids a ConcurrentModificationException
Try this code, it works OK:
private static CachedObjectsClass singletonInstance = null;
HashMap<String, Object> cachedObjects;
private CachedObjectsClass()
{
cachedObjects = new HashMap<>();
}
public static CachedObjectsClass getInstance()
{
singletonInstance = singletonInstance == null ? new CachedObjectsClass()
: singletonInstance;
return singletonInstance;
}
public void addObjectToCache(String key, Object object)
{
cachedObjects.put(key, object);
}
public Object checkCacheForObject(String title)
{
return cachedObjects.get(title);
}
And usage:
Object cachedObjectCheck = CachedObjectsClass.getInstance()
.checkCacheForObject("kk");
CachedObjectsClass.getInstance().addObjectToCache("l", object);
I've done some fancy wrapping to avoid unchecked warnings in the past, but after 90 mins of poring over http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html, I can't write the findMatch method below and make it work without #SuppressWarnings("unchecked"). The parameterized class isn't known at compile time.
public interface Matchable<T>
{
public boolean matches(T toMatch);
}
public class PlaceForMatching
{
public static Object findMatch(Object toMatch, Object[] toSearch)
{
if(!(toMatch instanceof Matchable)) return null;
Matchable matchObj = (Matchable)toMatch;
Class<?> matchClass = matchObj.getClass();
for(Object obj : toSearch)
{
/**
* Check here verifies that the search list object we're about
* to check is the same class as the toMatch object.
* This means Matchable will work without a ClassCastException.
**/
if(matchClass.isInstance(obj) && matchObj.matches(obj))
return obj;
}
//Didn't find it
return null;
}
}
Note the code works because in every case Matchable is implemented by T.
Apple implements Matchable<Apple>
Orange implements Matchable<Orange>
EDIT: Add some test code
public static void main(String[] args)
{
Object[] randomList = createAppleArray();
Object apple = new Apple("Red");
Object match = findMatch(apple, randomList);
}
private static Object[] createAppleArray()
{
return new Object[] { new Apple("Pink"), new Apple("Red"), new Apple("Green") };
}
public class Apple implements Matchable<Apple>
{
String color;
public Apple(String color)
{
this.color = color;
}
public boolean matches(Apple apple)
{
return color.equals(apple.color);
}
}
public static <T extends Matchable<T>> T findMatch(T toMatch, T[] toSearch) {
if (toMatch == null)
return null;
Matchable<T> matchObj = toMatch;
Class<?> matchClass = matchObj.getClass();
for (T obj : toSearch) {
if (matchClass.isInstance(obj) && matchObj.matches(obj))
return obj;
}
return null;
}