;How can I use the value of a String variable as attribute or method name?
Want to do something like that:
class Person {
public String firstname;
}
String myAttributeName="firstname";
Person obj = new Person();
String firstNameOfObj = obj.{myAttributeName};
if you really want to do this, you could use reflection:
Person obj = new Person();
Method method = Person.class.getMethod("getFirstname");
String firstname = method.invoke(obj);
but as mentioned in the comments, you better use a map to hold attribute values:
class Person {
private Map<String,Object> attrs = new HashMap<>();
public void setAttribute(String attr, Object value)
{
attrs.put(attr,vaue);
}
public Object getAttribute(String attr)
{
attrs.get(attr);
}
}
Person person = new Person();
person.setAttribute("firstname","patrick");
String firstname = (String)person.getAttribute("firstname");
Related
I am trying to find string with max length of a given attribute of java. I will pass the attribute name as string into the method which will return me the string value of max length.
class Employee {
private String name;
private String designation;
private List<Address> address;
private ContactInfo contactInfo;
....
getter setter
}
class Address {
private String city;
private String state;
private String country;
......
getter setter
}
class ContactInfo {
private String mobileNumber;
private String landlineNumber;
....
getter setter
}
I have some data just like below:
ContactInfo contactInfo = new ContactInfo("84883838", "12882882");
Address address1 = new Address("city111", "state111", "country111");
Address address2 = new Address("city111111", "state11112", "country1112");
Employee employee1 = new Employee("xyz", "uyyy", List.of(address1, address2), contactInfo);
private String findStringWithMaxLength(String attribute) {
return employeeList.stream()
....
}
In above case, if I provide attribute value as "city" then it should return me the value "city111111" because of maximum string length.
If we have child objects and list of objects, how do I traverse with the given attribute.
You can create a method that take a list of employees and a function to get the specific attribute like this:
private String findStringWithMaxLength(List<Employee> employees, Function<Employee, String> function) {
return employees.stream()
.map(function)
.max(Comparator.comparing(String::length))
.orElseThrow(() -> new IllegalArgumentException("Empty list"));
}
and to call the method you can use:
findStringWithMaxLength(employees, Employee::getName)
findStringWithMaxLength(employees, Employee::getDesignation)
findStringWithMaxLength(employees, Employee::getAddress)
Note that the method will throw an exception if the list is empty, if you wont throw an exception, then you can replace it with orElse(withDefaultValue)
You can do it using reflection but here is a better "typesafe" way.
Let the class:
#Getter
#Setter
#AllArgsConstructor
static class Employee {
private String name;
private String designation;
private String address;
}
with getters and let the list
static List<Employee> employeeList = asList(
new Employee("xyz1", "abc1234", "address 123"),
new Employee("xyz123", "abc123", "address 1234"),
new Employee("xyz1234", "abc12", "address 12")
);
then, you can define a generic function able to traverse any String field
static Optional<String> findStringWithMaxLength(Function<Employee, String> getter) {
return employeeList.stream().map(getter).max(Comparator.comparingInt(String::length));
}
now, you can apply every getter to that function
for(Function<Employee, String> getter: Arrays.<Function<Employee, String>>asList(
Employee::getName,
Employee::getDesignation,
Employee::getAddress))
System.out.println(findStringWithMaxLength(getter));
with output
Optional[xyz1234]
Optional[abc1234]
Optional[address 1234]
(the optional is required since the list could be empty).
The given answers work fine. I'd like to use an enum in this case. If a method changes in the Employee class, you only have to change the enum, not every call using it:
enum EmployeeField {
NAME(Employee::getName),
DESIGNATION(Employee::getDesignation),
ADDRESS(Employee::getAddress);
private final Function<Employee, String> getter;
EmployeeField(Function<Employee, String> getter) {
this.getter = getter;
}
public Function<Employee, String> getGetter() {
return getter;
}
}
private static final List<Employee> employeeList = Arrays.asList(
new Employee("xyz1", "abc", "address 1"),
new Employee("xyz123", "abc", "address 1"),
new Employee("xyz1234", "abc", "address 1")
);
public static void main(String[] args) {
Optional<String> longestName = findStringWithMaxLength(EmployeeField.NAME);
if (longestName.isPresent()) {
System.out.println("Longest name = " + longestName.get());
} else {
System.out.println("No longest name");
}
}
private static Optional<String> findStringWithMaxLength(EmployeeField employeeField) {
return employeeList.stream()
.map(employee -> employeeField.getGetter().apply(employee))
.max(Comparator.comparing(String::length));
}
EDIT for your city use case, something along those lines:
Add an enum AddressField on the same model as the EmployeeField
enum AddressField {
CITY(Address::getCity);
....
}
then add a method
private static Optional<String> findStringWithMaxLength(List<Address> addressList, AddressField addressField) {
return addressList.stream()
.map(employee -> addressField.getGetter().apply(employee))
.max(Comparator.comparing(String::length));
}
and then add a CITY enum to your EmployeeField enum:
LANDLINE_NUMBER(employee -> employee.getContactInfo().getLandlineNumber()),
CITY(employee -> findStringWithMaxLength(employee.getAddress(), AddressField.CITY).get());
I'm trying to parameterize a string and set that string as the result of a model:
SomePanel.java
public SomePanel( String id, IModel<Person> personModel)
{
tallLabel = new Label( "height", new LoadableDetachableModel() {
pubic String load() {
Person person = personModel.getObject();
boolean isTall = apiCallToCheckIfTall( person );
// 'name' is a property on PersonModel
String name = person.getName();
String tallString = MessageFormat.format(getString("Tall.Label"), name );
String shortString = MessageFormat.format(getString("Short.Label"), name );
return isTall ? tallString : shortString;
}
});
add(tallLabel);
}
Text.properties
Tall.Label = ${name} is tall.
Short.Label = ${name} is short.
I tried implementing a solution but contact.getName() produces an error. My understanding is that personModel.getObject() would give me the actual object (which has getter getName defined) so not sure why this would produce an error.
MessageFormat uses indexed parameters, so you probably mixed up some technologies here.
Here's the simplest solution using Wicket's resource messages with names parameters:
return getString(isTall ? "Tall.Label" : "Short.Label", personModel)
I managed to get it to work with:
SomePanel.java
public SomePanel( String id, IModel<Person> personModel)
{
tallLabel = new Label( "height", new LoadableDetachableModel() {
public String load() {
Person person = personModel.getObject();
boolean isTall = apiCallToCheckIfTall( person );
PersonGetter getter = new PersonGetter ( personModel );
String name = getter.getName();
String RTStringModel = MessageFormat.format( getString("Tall.Label"), person.getName() );
String StringModel = MessageFormat.format( getString("Short.Label"), person.getName() );
return isTall ? RTStringModel : StringModel;
}
});
add(tallLabel);
}
...
private class NameGetter implements Serializable
{
private final IModel<Person> model;
public NameGetter( final IModel<Person> personModel )
{
this.model = person;
}
public String getName()
{
return getFormattedLegalName( this.model.getObject() );
}
}
public static final String getFormattedLegalName( Person person )
{
if ( person == null )
{
return "";
}
else
{
return person.getName();
}
}
Text.properties
Tall.Label = {0} is tall.
Short.Label = {0} is short.
This seems to be a bit too much to extract a value from the model though. I couldn't get the name from the personModel directly (e.g. personModel.getObject().getName()) and went the convoluted route of having to create another class.
I'm having a possible solution to the problem in my head but I don't quite know how to do that with code. I got stuck with invoking a method in a method in Java.
I have this code:
public Student getStudent(String queryWord){
//the queryWord here should actually be the String result that returnQueryColumn returns
}
private static Map<String, String> returnMap(String myQuery){
String[] params = myQuery.split("=");
Map<String, String> myMap = new HashMap<String, String>();
String myKey = params[0];
String myValue = params[1];
//myKey should be for example firstName, myValue should be the ACTUAL first name of the student
myMap.put(myKey,myValue);
return myMap;
}
private static String returnQueryColumn(Map<String, String> myMap){
//here I want to get just the KEY from the myMap(from the returnMap method)
//that key should be the column in my database, so I need this so that later on I can compare if the given key (firstName) is present in the database as a column
String queryWord = returnMap().get(); //query should get firstName in my case
return queryWord;
}
I know this code doesn't work, but I need some help, how can I achieve what I have in mind? I'm stuck at this - how can I invoke a method in other method, and make the string that is being returned in the first method to be a parameter in the second one.
Let's say you have Student class:
public class Student {
String fullName;
public Student(String fullName) {
this.fullName = fullName;
}
}
And, if I understood your intentions right, Main class can look like this.
Sample code prints student fullName property.
public class Main {
public static void main(String[] args) {
Student student = getStudent("john=John Appleseed");
System.out.println(student.fullName);
}
public static Student getStudent(String myQuery) {
return returnQueryColumn(myQuery);
}
private static Map<String, Student> returnMap(String myQuery){
String[] params = myQuery.split("=");
Map<String, Student> myMap = new HashMap<String, Student>();
String myKey = params[0];
String myValue = params[1];
Student student = new Student(myValue);
myMap.put(myKey, student);
return myMap;
}
private static Student returnQueryColumn(String myQuery) {
String[] params = myQuery.split("=");
String key = params[0];
return returnMap(myQuery).get(key);
}
}
I'm trying to create an instance of an object from a json string.
This is my object:
public class Person {
String name;
String address;
}
and this is my converter:
Gson gson = new Gson();
Person p = gson.fromJson(str, Person.class);
The problem is that my input string format can be more complex than my Person object, for example:
{
"name":"itay",
"address":{
"street":"my street",
"number":"10"
}
}
Or address's value can be a simple string (in that case I have no problem).
I want p.address to contain the json object as string.
This is only an example of my problem, in fact, the "address" is much more complex and the structure is unknown.
My solution is changing the Person class to:
public class BetterPerson {
String name;
Object address;
}
Now, address is an object and I can use .toString() to get the value.
Is there a better way of doing this?
You can try with JsonDeserializer to deserializer it as per JSON structure that is determined at run-time.
For more info have a look at GSON Deserialiser Example
Sample code:
class Person {
private String name;
private Object address;
// getter & setter
}
class Address {
private String street;
private String number;
// getter & setter
}
...
class PersonDeserializer implements JsonDeserializer<Person> {
#Override
public Person deserialize(final JsonElement json, final Type typeOfT,
final JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
Person person = new Person();
person.setName(jsonObject.get("name").getAsString());
JsonElement jsonElement = jsonObject.get("address");
if (!jsonElement.isJsonObject()) {
String address = jsonElement.getAsString();
person.setAddress(address);
} else {
JsonObject addressJsonObject = (JsonObject) jsonElement;
Address address = new Address();
address.setNumber(addressJsonObject.get("number").getAsString());
address.setStreet(addressJsonObject.get("street").getAsString());
person.setAddress(address);
}
return person;
}
}
Person data = new GsonBuilder()
.registerTypeAdapter(Person.class, new PersonDeserializer()).create()
.fromJson(jsonString, Person.class);
if (data.getAddress() instanceof Address) {
Address address = (Address) data.getAddress();
} else {
String address = (String) data.getAddress();
}
You can try HashMap<String,String> address without using extra Address POJO class as well if it's structure is also not known.
you can do in this way as well where if address is String then construct Address object and set the address string in street variable as illustrated below:
class Person {
private String name;
private Address address;
// getter & setter
}
...
JsonElement jsonElement = jsonObject.get("address");
if (!jsonElement.isJsonObject()) {
String address = jsonElement.getAsString();
Address obj = new Address();
obj.setStreet(address);
person.setAddress(obj);
}else{...}
This is the scenario:
<family>
<person>name</person>
<person>
<address> street </adress>
<address> street </address>
</person>
</family>
The person value can be a list of addresses or just the person name.
I think the solution is to use a converter but how do you do that?
Check what input you retrieve?
Tell the converter to just continue using the defaults for class 3?
Example class : (do note that this is for illustration)
public class Person {
private String name;
private List<Address> address;
}
public class Address {
private String street;
}
You do need to use a converter. Here is the converter for your example:
public class PersonConverter implements Converter {
public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
Person person = (Person) value;
if(person.name != null){
writer.setValue(person.name);
} else if(person.address != null){
for (Address address : person.address){
writer.startNode("address");
writer.setValue(address.street);
writer.endNode();
}
}
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
Person person = new Person();
person.name = reader.getValue();
if(person.name.trim().length()==0){
person.name = null;
}
List<Address> addresses = getAddress(reader, new ArrayList<Address>());
person.address = addresses;
if(person.address.size() == 0){
person.address = null;
}
return person;
}
private List<Address> getAddress(HierarchicalStreamReader reader, List<Address> addresses){
if (!reader.hasMoreChildren()){
return addresses;
}
reader.moveDown();
if(reader.getNodeName().equals("address")){
addresses.add(new Address(reader.getValue()));
reader.moveUp();
getAddress(reader, addresses);
}
return addresses;
}
public boolean canConvert(Class clazz) {
return clazz.equals(Person.class);
}
}
Here is the main method:
public static void main(String[] args) {
List<Person> persons = new ArrayList<Person>();
persons.add(new Person("John"));
List<Address> adds = new ArrayList<Address>();
adds.add(new Address("123 street"));
adds.add(new Address("456 street"));
persons.add(new Person(adds));
Family family = new Family(persons);
XStream stream = new XStream();
stream.registerConverter(new PersonConverter());
stream.processAnnotations(new Class[]{Family.class});
String xml = stream.toXML(family);
System.out.println(xml);
Family testFam = (Family) stream.fromXML(xml);
System.out.println("family.equals(testFam) => "+family.equals(testFam));
}
If you implement the equals method for the Family, Person, and Address classes it should print that they are equal at the end of the method when ran. Also worth noting is that I used some Annotations on the Family. I used #XStreamAlias("family") on the Class itself, and then on the collection of Person objects I used #XStreamImplicit(itemFieldName="person").
And here is my ouput when I run the main method supplied:
<family>
<person>John</person>
<person>
<address>123 street</address>
<address>456 street</address>
</person>
</family>
family.equals(testFam) => true