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;
}
Related
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.
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.
In an attempt to create a N-ary tree with multiple node with different type of node objects[Country | State etc], I tried modifying the below generic class from -
https://github.com/vivin/GenericTree/blob/master/src/main/java/net/vivin/GenericTreeNode.java
I tried the following -
package com.mycompany.ds;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GenericTreeNode<T>{
private T data;
private List<GenericTreeNode<? super T>> children;
private GenericTreeNode<? super T> parent;
public GenericTreeNode() {
super();
children = new ArrayList<GenericTreeNode<? super T>>();
}
public GenericTreeNode(T data) {
this();
setData(data);
}
public GenericTreeNode<? super T> getParent() {
return this.parent;
}
public List<GenericTreeNode<? super T>> getChildren() {
return this.children;
}
public int getNumberOfChildren() {
return getChildren().size();
}
public boolean hasChildren() {
return (getNumberOfChildren() > 0);
}
public void setChildren(List<GenericTreeNode<? super T>> children) {
for(GenericTreeNode<? super T> child : children) {
child.parent = this;
}
this.children = children;
}
public void addChild(GenericTreeNode<? super T> child) {
child.parent = this;
children.add(child);
}
public void addChildAt(int index, GenericTreeNode<T> child) throws IndexOutOfBoundsException {
child.parent = this;
children.add(index, child);
}
public void removeChildren() {
this.children = new ArrayList<GenericTreeNode<? super T>>();
}
public void removeChildAt(int index) throws IndexOutOfBoundsException {
children.remove(index);
}
public GenericTreeNode<? super T> getChildAt(int index) throws IndexOutOfBoundsException {
return children.get(index);
}
public T getData() {
return this.data;
}
public void setData(T data) {
this.data = data;
}
public String toString() {
return getData().toString();
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
GenericTreeNode<?> other = (GenericTreeNode<?>) obj;
if (data == null) {
if (other.data != null) {
return false;
}
} else if (!data.equals(other.data)) {
return false;
}
return true;
}
/* (non-Javadoc)
* #see java.lang.Object#hashCode()
*/
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((data == null) ? 0 : data.hashCode());
return result;
}
public String toStringVerbose() {
String stringRepresentation = getData().toString() + ":[";
for (GenericTreeNode<? super T> node : getChildren()) {
stringRepresentation += node.getData().toString() + ", ";
}
//Pattern.DOTALL causes ^ and $ to match. Otherwise it won't. It's retarded.
Pattern pattern = Pattern.compile(", $", Pattern.DOTALL);
Matcher matcher = pattern.matcher(stringRepresentation);
stringRepresentation = matcher.replaceFirst("");
stringRepresentation += "]";
return stringRepresentation;
}
}
But errors in the following methods -
public void setChildren(List<GenericTreeNode<? super T>> children) {
for(GenericTreeNode<? super T> child : children) {
child.parent = this;
}
this.children = children;
}
public void addChild(GenericTreeNode<? super T> child) {
child.parent = this;
children.add(child);
}
Errors -
1 - Type mismatch: cannot convert from GenericTreeNode<T> to GenericTreeNode<? super capture#2-of ? super
T>
2 - Type mismatch: cannot convert from GenericTreeNode<T> to GenericTreeNode<? super capture#4-of ? super
T>
How can I fix these?
You could create a class / interface that represents a GISEntity and create the generic tree node whose generic type T extends GISEntity. This would allow you to have nodes of different kinds of GISEntity subclasses-- Country / State etc.
To build up on the answer of ditkin:
after having made all your classes implement or extend GISEntity, you would write your tree this way:
public class GenericTreeNode<T extends GISEntity>{
private T data;
private List<GenericTreeNode<? extends GISEntity>> children;
private GenericTreeNode<? extends GISEntity> parent;
public GenericTreeNode() {
super();
children = new ArrayList<GenericTreeNode<? extends GISEntity>>();
}
////////
......
////////
public void addChild(GenericTreeNode<? extends GISEntity> child) {
child.parent = this;
children.add(child);
}
public void addChildAt(int index, GenericTreeNode<? extends GISEntity> child) throws IndexOutOfBoundsException {
child.parent = this;
children.add(index, child);
}
////////
......
////////
}
Note that it will not really help you to avoid class casting. The thing is that as soon as you have added children to your node, when you retrieve them you just know that they are GISEntity, because of type erasure. So this technique only give you a bit of type safety.
It's not a good idea to use Generic in order to store different types of objects in the same collection. What you should do is to create an hierarchy and use it to store your objects. With a good design, the base class will have all that's necessary to access the different objects without casting; otherwise you will have to write some cast here and there. Here is an example of code (please note that the design here is far from beeing optimal and is simply to show the use of virtual function and polymorphism) :
static class GISEntity {
final String name;
public GISEntity (String name) { this.name = name; }
public String getName() { return name; }
public String getTypeName() { return "GISEntity"; }
public String toString() { return name; }
}
//
static class Country extends GISEntity {
final String typeName = "country";
public Country (String name) { super(name); }
public String getTypeName() { return typeName; }
public String toString() { return name; }
}
//
static class State extends GISEntity {
public State (String name) { super(name); }
public String getTypeName() { return "state"; }
public String toString() { return name; }
}
//
static class Territory extends GISEntity {
public Territory (String name) { super(name); }
public String getTypeName() { return "territory"; }
public String toString() { return name; }
}
//
// Here's an example of subclassing GenericTreeNode<GISEntity>:
//
static class IsATerritory extends GenericTreeNode<GISEntity> {
IsATerritory (String name) { super (new Territory (name)); }
public GISEntity getData() {
State s = new State (super.getData().getName().toUpperCase());
return s; }
};
//
// Here we put some data. Note that the order of insertion is important
// for the tree and that it's not alphabetical in this example.
//
GenericTree<GISEntity> earth = new GenericTree<GISEntity>() ;
//
GenericTreeNode<GISEntity> ListOfCountries = new GenericTreeNode<GISEntity>(new GISEntity("List of countries"));
//
GenericTreeNode<GISEntity> US = new GenericTreeNode<GISEntity>(new Country("United States"));
GenericTreeNode<GISEntity> Washington = new GenericTreeNode<GISEntity>(new State("Washington"));
GenericTreeNode<GISEntity> Florida = new GenericTreeNode<GISEntity>(new State("Florida"));
//
GenericTreeNode<GISEntity> Canada = new GenericTreeNode<GISEntity>(new Country("Canada"));
//
// We are now using some different ways for creating the nodes:
//
#SuppressWarnings("unchecked")
List<GenericTreeNode<GISEntity>> CanadaProvinces = new ArrayList<GenericTreeNode<GISEntity>>(
Arrays.asList(new GenericTreeNode<GISEntity>(new State("Quebec")),
new GenericTreeNode<GISEntity>(new State("Ontario")))
);
//
US.addChild(Washington);
US.addChild(Florida);
//
// Here's are two examples of subclassing; this time with anonymous classes.
// Don't forget that these two anonymous classes will hold an hidden reference
// to the outer classe as they are not static!
//
GenericTreeNode<GISEntity> alberta = new GenericTreeNode<GISEntity>() {
{ setData(new State ("Alberta")); }
public GISEntity getData() {
State s = new State (super.getData().getName().toUpperCase());
return s;
}
};
//
GenericTreeNode<GISEntity> saskatchewan = new GenericTreeNode<GISEntity>(new State ("saskatchewan")) {
public GISEntity getData() {
State s = new State (super.getData().getName().toUpperCase());
return s; }
};
//
CanadaProvinces.add(alberta);
CanadaProvinces.add(saskatchewan);
//
// Other ways for creating the nodes:
CanadaProvinces.add(new GenericTreeNode<GISEntity>(new State("Manitoba")));
//
// Note the use of the IsATerritory subclass:
CanadaProvinces.add(new IsATerritory("Northwest Territories"));
//
Canada.setChildren(CanadaProvinces);
//
ListOfCountries.addChild(Canada);
ListOfCountries.addChild(US);
//
earth.setRoot(ListOfCountries);
//
System.out.println(earth.toString());
System.out.println();
System.out.println(earth.toStringWithDepth());
System.out.println();
System.out.println(ListOfCountries.toStringVerbose());
//
List<GenericTreeNode<GISEntity>> loc = earth.build(GenericTreeTraversalOrderEnum.PRE_ORDER);
System.out.println(loc);
//
Map<GenericTreeNode<GISEntity>, Integer> locd = earth.buildWithDepth(GenericTreeTraversalOrderEnum.PRE_ORDER);
System.out.println(locd);
//
Map<GenericTreeNode<GISEntity>, Integer> locd2 = earth.buildWithDepth(GenericTreeTraversalOrderEnum.POST_ORDER);
System.out.println(locd2);
//
// Two examples of iteration; showing both the use of the instanceof operator
// and of virtual (or override) functions:
//
for (GenericTreeNode<GISEntity> gen: loc) {
GISEntity data = gen.getData();
if (data instanceof State) {
System.out.println("Is State: " + data.getName());
} else if (data instanceof Country) {
System.out.println("Is Country: " + data.getName());
} else {
System.out.println(data.getTypeName() + data.getName());
}
}
//
for (Entry<GenericTreeNode<GISEntity>, Integer> entry: locd.entrySet()) {
GISEntity data = entry.getKey().getData();
Integer depth = entry.getValue();
if (data instanceof State) {
System.out.println(depth.toString() + ": Is State: " + data.getName());
} else if (data instanceof Country) {
System.out.println(depth.toString() + ": Is Country: " + data.getName());
} else {
System.out.println(depth.toString() + ": " + data.getTypeName() + data.getName());
}
}
In this example, I have subclassed the class GenericTreeNode in three different ways (two anonymous classes, one a named class) in order to change the getData so that it will return a new GISEntity where the name has been replaced with its UpperCase copy.
Note that will all these three subclasses, I'm using GenericTreeNode<GISEntity> and not something like GenericTreeNode<Territory>. This is because that even if Territory is a subclass of GISEntry, the class GenericTreeNode<Territory> is not a subclass of GenericTreeNode<GISEntry>.
For using something like a mix of GenericTreeNode<Territory> with GenericTreeNode<GISEntry>, we have to use the ? extends GISEntry and ? super GISEntry and this will multiply by one thousand the complexity of the generic code. Unless that you want to make some heavy subclassing of the generic classes GenericTree<> and GenericTreeNode<>, it's totally useless to use the ? type; even for a collecting different types of objects. Unless that you have years of experience in generic code, don't use the ? notation. Most projects will do totally fine with the simpler generic code.
I've also added some examples of iterations over the generic tree for both the build() and the buildWithDepth() functions for those interested.
Finally, as a reference, this generic tree is explained in http://vivin.net/2010/01/30/generic-n-ary-tree-in-java/ (3 pages).
I Need a java function which converts from java.util.List to java.util.Set and vice versa, independent of type of objects in the List/Set.
Like List.addAll and Set.addAll?
Most of the class of the java collection framework have a constructor that take a collection of element as a parameter. You should use your prefered implementation ton do the conversion for exameple (with HashSet and ArrayList):
public class MyCollecUtils {
public static <E> Set<E> toSet(List<E> l) {
return new HashSet<E>(l);
}
public static <E> List<E> toSet(Set<E> s) {
return new ArrayList<E>(s);
}
}
public static <E> Set<E> getSetForList(List<E> lst){
return new HashSet<E>(lst);//assuming you don't care for duplicate entry scenario :)
}
public static <E> List<E> getListForSet(Set<E> set){
return new ArrayList<E>(set);// You can select any implementation of List depending on your scenario
}
Instead of one function you can have two function to implement this functionality:
// Set to List
public List setToList(Set set) {
return new ArrayList(set);
}
// List to Set
public Set listToSet(List list) {
return new HashSet(list);
}
In a single function:
public Collection convertSetList(Collection obj) {
if (obj instanceof java.util.List) {
return new HashSet((List)obj);
} else if(obj instanceof java.util.Set) {
return new ArrayList((Set)obj);
}
return null;
}
Example: (updated)
public class Main {
public static void main(String[] args) {
Set s = new HashSet();
List l = new ArrayList();
s.add("1");s.add("2");s.add("3");
l.add("a");l.add("b");l.add("c");
Collection c1 = convertSetList(s);
Collection c2 = convertSetList(l);
System.out.println("c1 type is : "+ c1.getClass());
System.out.println("c2 type is : "+ c2.getClass());
}
public static Collection convertSetList(Collection obj) {
if (obj instanceof java.util.List) {
System.out.println("List!");
return (Set)new HashSet((List) obj);
} else if (obj instanceof java.util.Set) {
System.out.println("Set!");
return (List)new ArrayList((Set) obj);
} else {
System.out.println("Unknow type!");
return null;
}
}
}