Edit: I was trying to simplify my problem at hand a little, but turns out, it created more confusion instead. Here's the real deal:
I am working with AWS's Java SDK for DynamoDB. Using the DynamoDBMapper class, I am trying to query DynamoDB to retrieve items from a particular table. I have several objects that map to my DynamoDB tables, and I was hoping to have a generic method that could accept the mapped objects, query the table, and return the item result.
Psudo-code:
#DynamoDBTable(tableName="testTable")
public class DBObject {
private String hashKey;
private String attribute1;
#DynamoDBHashKey(attributeName="hashKey")
public String getHashKey() { return this.hashKey; }
public void setHashKey(String hashKey)
#DynamoDBAttribute(attributeName="attribute1")
public String getAttribute1() { return this.attribute1; }
public void setAttribute1(String attribute1) { this.attribute1 = attribute1; }
}
public class DatabaseRetrieval {
public DatabaseRetrieval()
{
DBObject dbObject = new DBObject();
dbObject.setHashKey = "12345";
DBRetrievalAgent agent = new DBRetrievalAgent;
dbObject = agent.retrieveDBObject(dbObject.getClass(), dbObject);
}
}
public class DBRetrievalAgent {
public Object retrieveDBObject(Class<?> classType, Object dbObject)
{
DynamoDBQueryExpression<classType> temp = new DynamoDBQueryExpression<classType>().withHashKeyValues(dbObject);
return this.dynamoDBMapper.query(classType, temp);
}
}
Use a type witness within your method:
public <T> String getResult(Class<T> type) {
List<T> newList = new ArrayList<>();
//other code
}
Try this
ArrayList<T> newList = new ArrayList<>();
You can specify the type as T for your getResult() to make it generic (i.e., accepts any class) as shown below:
public <T> String getResult(T t) {
String result = "";
List<T> newList = new ArrayList<>();
// perform actions
return result;
}
Related
This question already has answers here:
Converting many 'if else' statements to a cleaner approach [duplicate]
(7 answers)
Closed 4 years ago.
I think this is a very common situation in web projects. Assume there is an entity such as:
//JAVA code
#Data
class Entity{
private String a;
private String aExt;
private String b;
private String bExt;
private String c;
private String cExt;
... something more ...
}
For some purpose, I need to get part of values from Entity according to a passed argument, like:
public ViewObject foo(Entity entity, String condition){
ViewObject vo = new ViewObject();
if("aRelated".equals(condition)){
vo.setValue1(entity.getA());
vo.setValue2(entity.getAExt());
}
else if("bRelated".equals(condition)){
vo.setValue1(entity.getB());
vo.setValue2(entity.getBExt());
}
else if(cRelated".equals(condition)){
vo.setValue1(entity.getC());
vo.setValue2(entity.getCExt());
}
... else statement if there are other values ....
return vo;
}
I know I can use switch-case statement to reduce some words in foo(), but there is no essential difference compared with if-else, especially when the Entity has many variables.
As a plain Example, foo() is only a view object builder, but my project is more complex which have many duplicated code with only different variable's name in each if-else statement.
How do I reduce the above duplicated code?
You can try creating two hash maps:
// name these properly!
HashMap<String, Function<Entity, String>> valueMap = new HashMap<>();
HashMap<String, Function<Entity, String>> extMap = new HashMap<>();
Add these KVPs:
// valueMap
"aRelated" - Entity::getA
"bRelated" - Entity::getB
"cRelated" - Entity::getC
// extMap
"aRelated" - Entity::getAExt
"bRelated" - Entity::getBExt
"cRelated" - Entity::getCExt
Now, you can do this without an if statement:
vo.setValue1(valueMap.get(condition).apply(entity));
vo.setValue2(extMap.get(condition).apply(entity));
Another option would be to use reflection:
import java.lang.reflect.Method;
import java.lang.reflext.InvocationTargetException;
...
public ViewObject foo(Entity e, String c) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
String[] methodNames = { "get" + c.substring(0,1).toUpperCase(), "get" + c.substring(0,1).toUpperCase() + "Ext" };
Method[] methods = { e.getClass().getDeclaredMethod(methodNames[0]), e.getClass().getDeclaredMethod(methodNames[1]) };
ViewObject vo = new ViewObject();
vo.setValue1((String)methods[0].invoke(e));
vo.setValue2((String)methods[1].invoke(e));
return vo;
}
Although I have to admit I personally like the map approach of the other answers more, just showing more options.
Use of a Map would do the trick:
class EntityPart {
String s;
String sExt;
}
class Entity {
Map<String,EntityPart> m = new HashMap<>();
m.add("aRelated",new EntityPart());
m.add("bRelated",new EntityPart());
....
}
public ViewObject foo(Entity entity, String condition) {
ViewObject vo = new ViewObject();
EntityPart ep = entity.m.get(condition);
vo.setValue1(ep.s);
vo.setValue2(ep.sExt);
return vo;
}
Make Entity as enum instead of class.
public enum Entity {
A("a", "aExt"), B("b", "bExt"), C("c", "cExt");
private final String name;
private final String text;
private Entity(String name, String text) {
this.name = name;
this.text = text;
}
public String getName() {
return name;
}
public String getText() {
return text;
}
public static Entity fromString(String raw) {
return LOOKUP.get(raw);
}
private static final Map<String, Entity> LOOKUP = new HashMap<>();
static {
for (Entity e : values()) {
LOOKUP.put(e.getName(), e);
}
}
}
And modify your foo method as
public ViewObject foo(String condition){
/*
* pass condition as "a", "b", "c" only not "aRelated", "bRelated", "cRelated"
*
*/
ViewObject vo = new ViewObject();
Entity e = Entity.fromString(condition);
if(null != e) {
vo.setValue1(e.getName());
vo.setValue2(e.getText());
}
return vo;
}
I am trying to downcast a function for my database objects. I get:
The method GetAsStringArray(HashMap<Long,dbObject>) in the type
dbObject is not applicable for the arguments
(HashMap<Long,dbEmployee>)
when I try to call GetAsStringArray(). Here is my test code:
dbObject base class:
public class dbObject implements Comparable<dbObject> {
protected long id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public static dbObject[] GetAsArray(HashMap<Long,dbObject> map) {
Collection<dbObject> list = map.values();
dbObject[] ar = list.toArray(new dbObject[] {});
Arrays.sort(ar);
return ar;
}
public static String[] GetAsStringArray(HashMap<Long,dbObject> map) {
Vector<String>vStrings = new Vector<String>();
Collection<dbObject> list = map.values();
dbObject[] ar = list.toArray(new dbObject[] {});
Arrays.sort(ar);
for (dbObject o : ar)
vStrings.add(o.toString());
return (String[]) vStrings.toArray(new String[] {});
}
#Override
public int compareTo(dbObject another) {
return (int)(this.getId() - another.getId());
}
}
Child class dbEmployee:
public class dbEmployee extends dbObject {
private String First;
private String Last;
public dbEmployee(){}
public String toString() {
return Last + ", " + First;
}
}
And the error:
public static void main(String[] args)
{
HashMap<Long,dbEmployee>mapEmployees = new HashMap<Long,dbEmployee>();
dbEmployee.GetAsStringArray(mapEmployees);
}
Note: some of my dbObjects will have their own compareTo() function (a few of the objects have a field in the db to override the default sort order), but most of the items just sort by id.
You will need to redefine the method thusly:
public static dbObject[] GetAsArray(HashMap<Long, ? extends dbObject> map) {
Collection<? extends dbObject> list = map.values();
dbObject[] ar = list.toArray(new dbObject[] {});
Arrays.sort(ar);
return ar;
}
A Map<Long, dbEmployee> cannot be used in place of a Map<Long, dbObject>, because although it has some of the same behaviour, some of its behaviour is very different. In particular, you can't add an Object of type dbObject to a Map<Long, dbEmployee>.
If the method GetAsArray(HashMap<Long, dbObject> map) allowed you to pass it a Map<Long, dbEmployee>, you would be able to add dbObjects to the map from inside the method. The method has no way to know that the map is not actually a map of dbObjects. Then, if you held on to a reference to the map outside of the method, you would have big problems. The map would no longer by type-safe. It would say it was a Map<Long, dbEmployee>, but it would actually contain dbObjects that were not dbEmployees.
The ? extends dbObject syntax allows you to specify what you can get out of the map without detailing what can go in it. You'll notice that if you now try to add something to the map inside the GetAsArray(HashMap<Long, extends dbObject> map) method, you'll get a compile error.
(There is an equivalent syntax, ? super dbEmployee, that would let you put things in the map without knowing what could come out).
Basically, I marshall on one server into JSON, then send it to another server, where it should be unmarshalled. I use a response object called list wrapper, so that if there are any errors I can pass them along. With the JSON marshalled below by badgerfish(jettison), in a resteasy class, then returned to the other server, GSON will unmarshall to a listwrapper object, but the list inside is null. Any ideas?
Note: The list must remain generic because different objects may go into the list, though the list will always only have one type in it at a time.
Json
unmarshalling
GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();
Object List;
if (!JSON.equals("")) {
List = gson.fromJson(new BufferedReader(new StringReader(JSON)), ListWrapper.class);
}
Listwrapper type
#XmlRootElement(name = "ListWrapper")
public class ListWrapper {
private Vector<Object> objects;
private String status;
private int batch;
private ValidationException e;
public ListWrapper() {
this.setStatus("Success");
}
public ListWrapper(Vector<Object> list) {
this.setStatus("Success");
this.objects = list;
}
public ListWrapper(int x) {
this.setStatus("batch");
this.batch = x;
}
public Vector<Object> getList() {
return objects;
}
public void setList(Vector<Object> object) {
this.objects = object;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
#XmlJavaTypeAdapter(ThrowableAdapter.class)
public ValidationException getE() {
if (e != null) {
return e;
} else {
return null;
}
}
public void setE(ValidationException x) {
this.e = x;
}
public int getBatch() {
return batch;
}
public void setBatch(int batch) {
this.batch = batch;
}
}
You cannot deserialize using directly ListWrapper, you need a container class, this is why you list is empty. By the way, your list is not a list but a map instead (curly braces limit content of list).
I created a code that correctly parse your JSON and provides you with some simple functionality to extract data. Since you want to keep flexible the data you are passing between the servers, you will need strings to access to data.
Here is the code ready to copy and paste to try it by yourself. Keep in mind that accessor methods are based on structure you are showing in the example. I provided you in main 4 different kind of data you can extract from it. Let me know if you need more information about that.
package stackoverflow.questions.q19817221;
import java.util.*;
import com.google.gson.Gson;
public class Q19817221 {
public class ListWrapper {
private Map list;
private Map status;
private Map batch;
private Object extractValue(Map m) {
return m.get("$");
}
public Integer getBatch() {
return Integer.valueOf( (String) extractValue(batch));
}
public Object getValueFromList(String key) {
try {
Map m = (Map) list.get(key);
if (m != null)
return extractValue(m);
} catch (Exception e) {
return list.get(key);
}
return null;
}
public Object getValueFromList(String secondLevelKey, String key) {
Map secondLevelMap = (Map) list.get(secondLevelKey);
try {
Map m = (Map) secondLevelMap.get(key);
if (m != null)
return extractValue(m);
} catch (Exception e) {
return list.get(key);
}
return null;
}
}
public class Container {
public ListWrapper ListWrapper;
}
/**
* #param args
*/
public static void main(String[] args) {
String json = "{\"ListWrapper\":{\"batch\":{\"$\":\"0\"},\"list\":{\"#xmlns\":{\"xsi\":\"http://www.w3.org/2001/XMLSchema-instance\"},\"#xsi:type\":\"fidsUsers\",\"createdBy\":{\"$\":\"administrator\"},\"createdWhen\":{\"$\":\"2013-02-25T17:29:19-05:00\"},\"endDate\":{\"$\":\"2016-10-28T00:00:00-04:00\"}," +
"\"isDisabled\":{\"$\":\"N\"},\"previousPasswords\":{\"$\":\"HXQDa4WxTdBmZtvhMVTgnw==####zW6bdHkKdMN2p6CgRNjNHA==####Sim7JN3kaHoXnh3KUS2++Q==####Emz7zU0Wrm0lyb/K522O5A==##ZirxzRl28JqfjOzIaMzAog==\"}," +
"\"primaryKey\":{\"$\":\"David\"},\"pswdChgDate\":{\"$\":\"2013-07-12T08:27:46-04:00\"},\"pswdCount\":{\"$\":\"0\"},\"roleId\":{\"$\":\"Admin\"},\"roleIdFidsRoles\":{\"globalAccess\":{\"$\":\"Y\"},\"primaryKey\":{\"$\":\"Admin\"},\"roleDesc\":{\"$\":\"Administrator\"},\"roleId\":{\"$\":\"Admin\"}," +
"\"updatedBy\":{\"$\":\"David\"}},\"startDate\":{\"$\":\"1992-07-28T00:00:00-04:00\"},\"updatedBy\":{\"$\":\"David\"},\"updatedWhen\":{\"$\":\"2013-10-02T10:46:31-04:00\"},\"userId\":{\"$\":\"David\"},\"userName\":{\"$\":\"David3\"},\"userPassword\":{\"$\":\"HXQDa4WxTdBmZtvhMVTgnw==\"}},\"status\":{\"$\":\"Success\"}}}";
Container c = new Gson().fromJson(json, Container.class);
ListWrapper lw = c.ListWrapper;
System.out.println("batch:" + lw.getBatch());
System.out.println("createdBy:" + lw.getValueFromList("createdBy"));
System.out.println("#xsi:type: " + lw.getValueFromList("#xsi:type"));
System.out.println("roleIdFidsRoles\\primaryKey: " + lw.getValueFromList("roleIdFidsRoles", "primaryKey"));
}
}
This is execution result:
batch:0
createdBy:administrator
#xsi:type: fidsUsers
roleIdFidsRoles\primaryKey: Admin
By looking into your ListWrapper class and a json you have posted - you have a mismatch and that is probably why it failed to unmarshall.
For:
{"ListWrapper":{"batch":{"$":"0"},"list":{"#xmlns":{"xsi":"http:\/\/www.w3.org\/2001\/XMLSchema-instance"},"#xsi:type":"fidsUsers","createdBy":{"$":"administrator"},"createdWhen":{"$":"2013-02-25T17:29:19-05:00"},"endDate":{"$":"2016-10-28T00:00:00-04:00"},"isDisabled":{"$":"N"},"previousPasswords":{"$":"HXQDa4WxTdBmZtvhMVTgnw==####zW6bdHkKdMN2p6CgRNjNHA==####Sim7JN3kaHoXnh3KUS2++Q==####Emz7zU0Wrm0lyb\/K522O5A==##ZirxzRl28JqfjOzIaMzAog=="},"primaryKey":{"$":"David"},"pswdChgDate":{"$":"2013-07-12T08:27:46-04:00"},"pswdCount":{"$":"0"},"roleId":{"$":"Admin"},"roleIdFidsRoles":{"globalAccess":{"$":"Y"},"primaryKey":{"$":"Admin"},"roleDesc":{"$":"Administrator"},"roleId":{"$":"Admin"},"updatedBy":{"$":"David"}},"startDate":{"$":"1992-07-28T00:00:00-04:00"},"updatedBy":{"$":"David"},"updatedWhen":{"$":"2013-10-02T10:46:31-04:00"},"userId":{"$":"David"},"userName":{"$":"David3"},"userPassword":{"$":"HXQDa4WxTdBmZtvhMVTgnw=="}},"status":{"$":"Success"}}} to me it is a object ListWrapper that has 3 fields named: batch, list and status, where batch is an object with one field being a number, list is a map and status is an object having one string in it.
I am not familiar with badgerfish, having said that, in your ListWrapper try to change private Vector<Object> objects to private Map<String,Object> objects (and of course matching getters/setters) and see if that will work
The list must remain generic because different objects may go into
the list, though the list will always only have one type in it at a
time.
If you know what type is it before un-marshalling, then ONLY it can work through the use of TypeToken
Sample code :
Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);
gson.fromJson(json, fooType);
Also, the definition will change to something like this :
public class ListWrapper {
private Vector<T> objects;
private String status;
private int batch;
}
or
public class ListWrapper {
private Vector<T extends someBaseObject> objects;
private String status;
private int batch;
}
depends on how exactly you decide to implement.
I have an ArrayList of objects, which I need to sort using two attributes (using Comparators). I need to save the sorted output to a text file with a different name, depending on the attribute used to sort. For example, if the list is sorted by attribute1 then file will be attribute1.txt, if attribute2 the file will be attribute2.txt.
How I want it to work (pseudocode):
if(sortedByAtr1){
FileWriter fwstream = new FileWriter(sortedByAtribute1.getName()+".txt");
}
else(sortedByAtr2){
FileWriter fwstream = new FileWriter(sortedByAtribute2.getName()+".txt");
}
Is this possible?
I appreciate any advice.
Thanks.
Servo
Here's an object-oriented approach to solving this requirement.
Use a wrapper for the List and its sorting attribute:
public class ListSorter<V> {
private final List<V> values;
private String sortingAttribute;
public ListSorter(List<V> values) {
this.values = values;
}
public void sort(AttributeComparator<V> comparator) {
Collections.sort(values, comparator);
sortingAttribute = comparator.getSortingAttribute();
}
public String getSortingAttribute() {
return sortingAttribute;
}
}
Extend the Comparator interface so you can get your attribute name:
public interface AttributeComparator<T> extends Comparator<T> {
public String getSortingAttribute();
}
Create custom AttributeComparators like this:
public class FooBarComparator implements AttributeComparator<Foo> {
public int compare(Foo foo1, Foo foo2) {
// skipped nullchecks for brevity
return foo1.getBar().compare(foo2.getBar());
}
public String getSortingAttribute() {
return "bar";
}
}
Use:
List<Foo> yourList = new ArrayList<Foo>();
ListSorter<Foo> example = new ListSorter<Foo>(yourList);
AttributeComparator comparator1 = new FooBarComparator();
example.sort(comparator1);
FileWriter fwstream = new FileWriter(example.getSortingAttribute() +".txt");
I'm obviously missing something here, as this sound basic enough but yet...
I have a collection of objects . I need to use each one of them as parameter in constructor for a new object and return each new object to the caller method, one by one.
But -if I loop over the collection obviously the loop only runs once, and only returns the 1st object.
Edit : Returning the whole collection or some new collection will not work because :
The caller method [not mine to change] runs inside a start() method of a Runnable ThingProvider, which returns a single Thing whenever a request is submitted to it. So, returning List is not possible.
Thanks :)
public List<T> loop(Collection<? extends U> coll) {
List<T> a = new ArrayList<T>();
for (U u : coll){
a.add(new T(u));
}
return a;
}
Return a custom Iterator. Assumming your new objects are of class MyObject and the constructor accepts an Object:
public Iterator<MyObject> myObjectsIterator(final Iterator<? extends Object> it) {
return new Iterator<MyObject>() {
public boolean hasNext() {
return it.hasNext();
}
public MyObject next() {
return new MyObject(it.next());
}
public void remove() {
it.remove();
}
};
}
And you would call it like this:
...
Iterator<MyObject> myIt = myObjectsIterator(myListOfObjects.iterator());
// Now you can pass myIt around as a normal object. It will remember
// which one is the next Object with which to construct a MyObject
// and will generate it on the fly
...
while (myIt.hasNext()) { // is there any MyObject remaining?
MyObject myObj = myIt.next(); // gets the next MyObject
// do something with myObj
}
...
This is a poorly worded question and I think as others have noted, just returning a new list of the objects is fine. But if you really want to process them one at a time while you're looping through it, you can use the command pattern.
public interface Command {
void execute(NewType object);
}
Now in your caller method, you can do the following:
public void doSomething() {
processList(myList, new Command() {
void execute(NewType object) {
// Do whatever you want with this object
}
});
}
And, in the method that will actually go through the list:
public void processList(Iterable<OldType> values, Command command) {
for(OldType v : values) {
NewType newType = new NewType(v);
command.execute(newType);
}
}
In java you can return only once. So if you want to get some informations from your methods either you wrap them into a "Big" Object (here a List) or you give to the method the means to put informations in your parameters.
You could have something like this :
public static void main(String... args){
List<Parameter> parameters = methodToGetParameters();
List<Result> results = generateObjectsFromList(parameters);
for(Result result : results){
handleAResult(result);
}
}
public List<Result> generateObjectsFromList(List<Parameter> parameters){
List<Result> results = new ArrayList<Result>();
for(Parameter parameter : parameters){
results.add(new Result(parameter));
}
return results;
}
Or like this :
public static void main(String... args){
List<Parameter> parameters = methodToGetParameters();
List<Result> results = new ArrayList<Result>();
generateObjectsFromList(parameters, results);
for(Result result : results){
handleAResult(result);
}
}
public void generateObjectsFromList(List<Parameter> parameters, List<Result> results){
for(Parameter parameter : parameters){
results.add(new Result(parameter));
}
}
A third way to do this would be to use fields, but it's not really good to have a lot of fields if they're not really used (or only by one method).
On the same topic :
Java Object Oriented Design Question: Returning multiple objects in java(Updated)
Using a java method to return multiple values?
Return a collection from the method and in the collection implement a custom iterator to transform the input collection to the new collection. The following code shows how to do it using the Google Guava library:
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
public class Test {
static class Person {
public final String name;
public Person(String name) {
this.name = name;
}
}
public static Collection<Person> peopleFromNames(Collection<String> names) {
return Collections2.transform(names, new Function<String, Person>() {
public Person apply(String name) {
return new Person(name);
}});
}
public static void main(String[] args) {
List<String> names = Arrays.asList("Brian", "Albert", "Roger");
for (Person person : peopleFromNames(names)) {
System.out.println(person.name);
}
}
}
do you mean using of delegates something like below
public class Test {
private static class Person{
private final String name;
Person(String name){
this.name = name;
}
#Override
public String toString() {
return return name;
}
}
private interface Printer {
void print(Object object);
}
public static void main(String[] args) {
final String[] names = {"one", "two", "three"};
final ArrayList<Person> people = construct(names, new Printer() {
#Override
public void print(Object object) {
System.out.println(object.toString());
}
});
}
private static ArrayList<Person> construct(String[] names, Printer printer) {
ArrayList<Person> people = new ArrayList<Person>();
for (String name : names) {
printer.print(new Person(name));
}
return people;
}
}
It's Possible.
Check these Project for Java-yield , yield4Java, infomancers
If you're using this just once in your entire code, You're better off choosing a method from the other answers.
Return a list of the new objects.