I have made a class named Entity, and have the following code:
Entity zombie1 = new Entity();
I get input 'zombie' from a scanner, and then concatenate a number, based on level on the end of that, leaving 'zombie1' as the string... I want to be able to use that string and call
zombie1.shoot("shotgun");
but I can't seem to find a solution. I'd just do a if statement but I want to be able to create as many zombies as I want and not have to put in more if statements every single time.
I've read articles using reflection and forString but that doesn't seem to be what i'm looking for.
Any help would be nice.
Possible solutions are to use a Map<String, Entity> to be able to store and retrieve entities based on specific Strings. If you have a limited number of sub-types of Entity such as Zombies, Vampires, Victims, etc, you could have a Map<String, List<Entity>>, allowing you to map a String to a specific type of entity and then get that type by number.
e.g.,
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Foo002 {
private static final String ZOMBIE = "zombie";
public static void main(String[] args) {
Map<String, List<Entity>> entityMap = new HashMap<String, List<Entity>>();
entityMap.put(ZOMBIE, new ArrayList<Entity>());
entityMap.get(ZOMBIE).add(new Entity(ZOMBIE, "John"));
entityMap.get(ZOMBIE).add(new Entity(ZOMBIE, "Fred"));
entityMap.get(ZOMBIE).add(new Entity(ZOMBIE, "Bill"));
for (Entity entity : entityMap.get(ZOMBIE)) {
System.out.println(entity);
}
}
}
class Entity {
private String type;
private String name;
public Entity(String type, String name) {
this.type = type;
this.name = name;
}
public String getType() {
return type;
}
public String getName() {
return name;
}
#Override
public String toString() {
return type + ": " + name;
}
}
This is not your best bet. Your best bet is to have a Map;
// PLEASE LOOK INTO WHICH MAP WOULD BE BEST FOR YOUR CASE OVERALL
// HASHMAP IS JUST AN EXAMPLE.
Map<String, Entity> zombieHoard = new HashMap<String, Entity>;
String getZombieID( int id )
{
return String.format( "zombie%s", id );
}
String createZombie() {
String zid = getZombieID( Map.size() );
Map.put( zid, new Entity() );
return zid;
}
void sendForthTheHoard() {
createZombie();
createZombie();
String currentZombie = createZombie();
zombieHoard.get( currentZombie ).shoot( "blow-dryer" );
zombieHoard.get( getZombieID( 1 ) ).eatBrains();
}
Put your zombies in an ArrayList. Example:
ArrayList<Entity> zombies = new ArrayList<Entity>();
Entity zombie1 = new Entity();
zombies.add(zombie1);
Entity zombie2 = new Entity();
zombies.add(zombie2);
etc...
Then when it is time to call a certain zombie to the following:
zombies.get(1).shoot("shotgun");
If you are talking about dynamically invoking a method on an object, you can use Reflection to get the method object and invoke it (Note: I may have inadvertantly mixed up some C# syntax in this Java):
Entity zombie1 = new Entity();
Method shootMethod = Entity.class.getMethod("shoot", new Class[] { string.class });
shootMethod.invoke(zombie1, new Object[] { "shotgun" });
Related
How to print any class instance in Java? Similar to JSON.stringify() in Javascript. Not necessary JSON, any format of output will do.
public class User {
public String name, password;
public int age;
public ArrayList<String> phones;
public static void login() {
//do something
}
}
User X = new User;
X.name = "john connor";
X.password = "skynet";
X.age = "33";
X.phones.add("1234567");
X.phones.add("7654321");
System.out.println(printClass(X))
Should output something like:
{ name:"john connor", password: "skynet", age: "33", phones:
["1234567", "7654321"], login: void function() }
You can use Apache's commons-lang's ToStringBuilder.reflectionToString
Of course, reflection is slow, so only do this with your test code. for normal use, please use eclipse's "Source" menu -> generate toString() (or intellij's generate toString()) which gives you a decent string.
There could be many ways to achieve what you need. Though i would be interested in why you need.
Override the toString() method.
see: http://www.javapractices.com/topic/TopicAction.do?Id=55
If the generation algorithm gets too long, then consider a separate class say UserPrettyPrinter.
public interface UserPrettyPrinter {
string print(User);
}
public class PrintUserInJSON implements UserPrettyPrinter {
string print(User user) {
//implement the algo here
}
}
you can also implement:
public class PrintUserInXML implements UserPrettyPrinter {
string print(User user) {
//implement the algo here
}
}
Either in conjugation to number-2 or as a standalone class, you can write
public class PrintObjectBasicAlgo {
String print(Object obj) {
/* i write pseudo code here. just ask if you cannot implement this
this would help: http://docs.oracle.com/javase/tutorial/reflect/class/classMembers.html
Class class = Obj.getClass();
Filed[] allVariables = class.getAllFieldsByReflection();
ArrayList<String> keys = new ArrayList<String>;
ArrayList<String> values = new ArrayList<String>;
for(Field field : allVariables) {
Object value = reflectionGetValueOfField( field, obj );
keys.add( field.getName());
values.add(value.toString());
}
now that you have the keys and values, you can generate a string in anyway you like
*/
}
}
You may see Visitor Pattern. it might be helpful.
You have two options here. The simple one is just to override the toString function for your class. I dont see why you dont do this really. In this case its as simple as
String toString(){
return "{ name:\""+name+", password: \""+passowrd....
}
The second option is to use reflection. This would be slightly (though not really) better if you had some sorta external class used for "printing classes". The pseudo code for that would be
StringBuilder s = new StringBuidler();
for(Field f : fields){
s.append(f.getName() + "\" :\"" + f.get()+ "\"");
}
return s.toString();
However this would be costly as reflection always is. Also if you just properly override the toString functions in the first place your printClass function could literally just be
String printClass(Object o){ return o.toString();}
Which of course again begs the question of why do you need a printClass function?
One option is to use Google Gson.
import java.util.ArrayList;
import java.util.List;
import com.google.gson.Gson;
class Project {
private int year = 1987;
private String name = "ROBOCOP-1";
private boolean active = false;
private List<String> list = new ArrayList<String>() {
{
add("PROTECT THE INNOCENT");
add("UPHOLD THE LAW");
add("SERVE THE PUBLIC TRUST");
add("CLASSIFIED");
}
};
}
public class GsonExample {
public static void main(String[] args) {
Project obj = new Project();
Gson gson = new Gson();
String json = gson.toJson(obj);
System.out.println(gson.toJson(obj));
}
}
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 would like to define a method and by passing the enum, returns the mapped type based on the enum. So far I only work out this way:
public class Person {
HashMap<String, Object> mData;
void int getDetail(DetailInt detail){
Object data = mData.get(detail.name());
if(data instanceof Integer)
return (int)data;
return 0;
}
void String getDetail(DetailStr detail){
Object data = mData.get(detail.name());
if(data instanceof String)
return (String)data;
return "";
}
}
public enum DetailInt {
Age("age"), Weight("weight"), Height("height");
String columnName;
DetailInt(String columnName){
this.columnName= columnName;
}
}
public enum DetailStr {
FirstName("first_name"), LastName("last_name");
String columnName;
DetailStr (String columnName){
this.columnName= columnName;
}
}
So I can use the same method, but passing different enums to get the data with the type.
int age = person.getDetail(DetailInt.Age);
String firstName = person.getDetail(DetailStr.FirstName);
Now, what I would like to achieve is to merge both enums together, so I can call as below:
int age = person.getDetail(Detail.Age);
String firstName = person.getDetail(Detail.FirstName);
It is neater. However, I have tried generic type and interface, still cannot find the way to do it. Use below way is similar to what I want but this is not enum type.
abstract class Detail {
}
class DetailStr extend Detail {
}
interface Details {
DetailStr firstName = new DetailStr("first_name");
DetailStr lastName = new DetailStr("las_name");
DetailInt age = new DetailInt("age");
DetailInt weight = new DetailInt("weight");
DetailInt height = new DetailInt("height");
}
public class Person {
void int getDetail(DetailInt detail){
....
}
void String getDetail(DetailStr detail){
....
}
}
You can't do this in Java.
This is because a particular value of an enumerator has the same type as any other value of that enumerator. It's therefore not possible to construct an overloaded function since there's no type difference to act as a descriminator. (You cannot overload a function by return type difference alone.)
The obvious solution is to have two methods getDetailAsInt and getDetailAsString.
I'll share this approach that does not use enums, but it might be of some use to you:
public class Key<T> {
private String key;
...
}
public class Keys {
public static final Key FIRST_NAME = new Key<String>("first_name");
public static final Key AGE = new Key<Integer>("age");
}
public class Person {
public <T> T getDetail(Key<T> key) {
Object detail = mData.get(key.getKey());
return (T) detail;
}
}
I'm afraid it might not be possible to convert it to use enums, so you'd have to ensure no unwanted keys are created in some other way (package-private constructor etc.)
i have a domain class(DB):
public class PersonDoamin {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
i also have model class:
public class PersonBean extends PersonDoamin {
}
so when i go to DAOImpl class and query for List and transfer this list to List and return to users as i have interface method for List getAllPerson(). so my questions is here when i transfer all data from List. Here i have some utility method that copies from one bean to another like this:
List<PersonDoamin> list = PersonDAO.getAllPersons();
List<PersonBean> pbList = new ArrayList<PersonBean>();
/* this below logic is pretty much in the all DAO impl*/
for(PersonDoamin p : list){
PersonBean pb = new PersonBean();
CopyHelper.copyBean(p, pb);
pbList.add(pb);
}
return pbList;
can we replace the looping and copying and adding to another list and returning part with somekind of generic method which will take any object two list and loop thorugh one and add it to another passed List parameter and return it. something like below which is not perfect right now:
public static <T> List<T> listToArray(List<T> list,List<T> list2) {
for(T element : list){
list2.add(element);
}
return list2;
}
public static void main(String[] args) {
List<PersonDoamin> personList = new ArrayList<PersonDoamin>();
PersonDoamin p = new PersonDoamin();
p.setName("aj");
p.setAge("25");
personList.add(p);
List<PersonBean> personBeansToReturn = new ArrayList<PersonBean>();
Test.listToArray(personList , personBeansToReturn );
}
A bit off topic, your design seems a bit weird that you have "Domain" class and "Bean" class and have "Bean" extends "Domain"...
Anyway, come back to your question, what you are trying to do is:
You have a List<Domain>
You want to transform each Domain in the List into a Bean (by use of some util method)
Put the resulting Beans into a list and return
Let's go through it step by step.
(by the way, the listToArray method you wrote does not align with your original loop as it does not do the transformation (point 2). I guess it is typo?)
(all psuedo code as I don't have environment on hand to make it compile. Concept should be correct I guess)
Step 1: Util method for Person
One biggest problem of your original util method is that, it is illegal to put a Parent object instance to a List of Child (it should be easy to figure why by yourself).
The util method should look like this:
List<PersonBean> toBeans(List<PersonDomain> domains) {
List<PersonBean> beans = new ArrayList<>(domains.size());
for (PersonDomain domain: domains) {
PersonBean bean = new PersonBean();
CopyHelper.copyBean(domain, bean);
beans.add(bean);
}
return beans;
}
Step 2: Make it generic
The problem above is that it only works for Person. If you want to make it generic, you will also need to provide the function to transform Domain to Bean:
(Assume you are using Java8, should be trivial to make your own interface if you are using older version)
<D,B> List<B> toBeans(List<D> domains, Function<B,D> mapper) {
List<PersonBean> beans = new ArrayList<>(domains.size());
for (PersonDomain domain: domains) {
beans.add(mapper.apply(domain));
}
return beans;
}
so that you can use it by:
return toBeans(personDomains, (domain) -> {
PersonBean bean = new PersonBean();
CopyHelper.copyBean(domain, bean);
return bean;
});
(You may consider wrap the function if in most case you are going to use the CopyHelper way)
<D,B> List<B> toBeansByBeanCopy(List<D> domains, Class<B> beanClass) {
return toBeans(domains, (domain)-> {
B bean = beanClass.newInstance();
CopyHelper.copyBean(domain, bean);
return bean;
});
}
so that you can use it as
return toBeansByBeanCopy(personDomains, PersonBean.class);
Step 3: Java has done it for you
Actually what you are trying to do above, it is already provided by Java in Java 8. You can simply do:
return personDomains.stream()
.map(d -> {
PersonBean bean = new PersonBean();
CopyHelper.copyBean(domain, bean);
return bean;
})
.collect(Collectors.toList());
You may write a little method to use in the lambda expression if it is the standard way.
return personDomains.stream()
.map(BeanMapper.mapper(PersonBean.class))
.collect(Collectors.toList());
(Leave the implementation as your exercise)
If you're looking for a way to call new on a generic type, you can, sort of. You have to use reflection and call newInstance on the Class object. I don't know if this is going to be feasible for you.
Also, I don't see anyway of realistically implementing your bean copy method without using some heavy reflection as well. In the example below I faked by just casting to the required classes.
public class GenericCopyTest
{
public static void main( String[] args ) throws Exception
{
List<PersonDoamin> personList = new ArrayList<PersonDoamin>();
PersonDoamin p = new PersonDoamin();
p.setName( "aj" );
p.setAge( "25" );
personList.add( p );
List<PersonBean> personBeansToReturn = new ArrayList<PersonBean>();
copyAndDowncast( personList, personBeansToReturn, PersonBean.class );
System.out.println( personBeansToReturn );
}
public static <T,U extends T> List<U> copyAndDowncast( List<T> from,
List<U> to, Class<U> type )
throws InstantiationException, IllegalAccessException
{
for( T element : from ) {
U nu = type.newInstance();
copyBean( element, nu );
to.add( nu );
}
return to;
}
private static <X,Y extends X> void copyBean( X from, Y nu ) {
((PersonBean)nu).setName( ((PersonDoamin)from).getName() );
((PersonBean)nu).setAge( ((PersonDoamin)from).getAge() );
}
}
class PersonDoamin {
private String name;
private String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
#Override
public String toString()
{
return "PersonDoamin{" + "name=" + name + ", age=" + age + '}';
}
}
class PersonBean extends PersonDoamin {
#Override
public String toString()
{
return "PersonBean{" + getName() + ',' + getAge()+ '}';
}
}
Output:
run:
[PersonBean{aj,25}]
BUILD SUCCESSFUL (total time: 0 seconds)
Why not just use addAll() for this? It does what you're trying to do, and it's already part of the system library.
Remember you can add a PersonBean to a PersonDomain list, but not the other way around.
public class GenericCopyTest
{
public static void main( String[] args ) {
List<PersonDoamin> personList = new ArrayList<PersonDoamin>();
List<PersonBean> personBeansToReturn = new ArrayList<PersonBean>();
personList.addAll( personBeansToReturn );
personBeansToReturn.addAll( personList ); // <-- FAILS
// No suitable method found
}
}
class PersonDoamin {}
class PersonBean extends PersonDoamin {}
If you want to put more than one bean class in the same list,
how about creating the list with parent class PersonDoamin , and then, you can store both PersonDoamin and PersonBean classes.
public static void main(String[] args) {
List<PersonDoamin> personList = new ArrayList<PersonDoamin>();
PersonDoamin p = new PersonDoamin();
p.setName("aj");
p.setAge("25");
personList.add(p);
// Changed here. PersonBean => PersonDoamin
List<PersonDoamin> personBeansToReturn = new ArrayList<PersonDoamin>();
Test.listToArray(personList, personBeansToReturn);
// also you can insert PersonBean into the list
personBeansToReturn.add(new PersonBean());
}
I try to design one translation server.
There have one POJO class (RootClass)in my local systems. and there also have remote system to sent Remote object(RemoteClass) to my system.
The responsibility of this service is translate Remote class to Root class.
The issue is that: There so many types. e.g. more than 200 types. I need write huge if-else to do this translation:
I list some pseudocode code to describe this question.
public class RootClass {
public String type;
public String attr1;
public String attr2;
public String attr3;
public String attr4;
}
public class RemoteClass {
public String type;
public String attr1;
public String attr2;
public String attr3;
}
public class Translator{
public RootClass translate(RemoteClass remote) {
RootClass root = new RootClass();
if ("a".equals(remote.type )) {
root.type = "veryGood";
if ("one".equals(remote.attr1)) {
root.attr2 = "true";
}
if ("two".equals(remote.attr1)) {
root.attr3 = "true";
}
if ("1".equals(remote.attr1) && "2".equals(remote.attr2) ) {
root.attr4 ="good";
}
} else if ("b".equals(remote.type)) {
root.type = "good";
if ("one".equals(remote.attr1)) {
root.attr2 = "1";
} else if ("two".equals(remote.attr1)) {
root.attr2 ="2";
}
} else if ("c".equals(remote.type)) {
root.type = "good";
if (remote.attr2.indexOf(":") > 0 ) {
String[] strArray = remote.attr2.split(":");
root.attr2=strArray[0];
root.attr3=strArray[1];
}
}
}
}
The 2 object describe 1 thing with totally difference structure.
Root class is kernel of our system and impossible to strut and we also think this Root class is very suitable for local system. and for Remote class is come from 3-rd party systems which we have no permission to change. So this translation is become very hard.
What I plan to remove is create more than 200 adopter for translation:
e.g:
public class adopterA implements RootAdoper {
public RootClass translate(RemoteClass remote) {
RootClass root = new RootClass();
root.type="veryGood";
if ("one".equals(remote.attr1)) {
root.attr2 = "true";
}
if ("two".equals(remote.attr1)) {
root.attr3 = "true";
}
if ("1".equals(remote.attr1) && "2".equals(remote.attr2) ) {
root.attr4 ="good";
}
}
}
And put all of those into HasMap
Map<String, RootAdoper> map = new HashMap<String, RootAdoper>();
But still have 200 small class to wrap if/else, Is any good pattern or design to solve this complex issue? Thanks in advance.
What is the key in your map? if the key is the Remote.type then you can just do
rootClass = map.get(remote.type).translate(remote);
Which does get rid of the if/else if blocks. Just be sure to handle unknown/ untranslated regions or have a NullObject that doesn't translate or performs a default translation.
The technical name for this from the book Refactoring to Patterns is called "Replace Conditional Dispatcher with Command"
You still have to populate the map though. Perhaps one way to do this is to make all the RootAdoper interface an enum and all implementations the types in the enum. You can also add a new method to the enum to get the Remote.Type that each value can translate.
enum RootAdoper{
A{
#Overide
public RootClass translate(RemoteClass remote){
//...
}
#Override
public String getTypeToTranslate(){
return "A";
}
},
... // other types listed here similarly
;
abstract RootClass translate(RemoteClass remote);
abstract String getTypeToTranslate();
}
Then you can populate the Map like this
Map<String, RootAdoper> map = new HashMap<String, RootAdoper>();
for(RootAdoper adoper : RootAdoper.values(){
map.put(adoper.getTypeToTranslate(), adoper);
}