I have a class Car that has a Field named trunk. How can I retrieve that name only with the property that is assigned to it and without any fixed String.
Something working like this fiction would be great:
System.out.println(new Car().getTrunk().getField().getName());
Output:
trunk
I don't want to use a fixed String to retrieve the Field and it's name because that would not refactor well. If I decide to rename from trunk to boot I want this to be handled completely by my IDE's refactoring tool.
UPDATE Car class:
public class Car{
String trunk;
// getters + setters
}
BACKGROUND:
I want to use Primefaces' Dynamic Columns for a CRUD-UI for several entities which uses a columnTemplate containig the names of the Fields/properties to be evaluated by Expression Language.
Consider introducing a enum holding all the properties (without values), for example
enum CarProperty {
TRUNK, HOOD, WHATEVER;
}
and storing them in Car as EnumMap:
class Car {
private Map<CarProperty, String> propsToValues = new EnumMap<>(...);
public String getValue(CarProperty property) { ... }
}
property name could be accessed by
((CarProperty) anyPropery).toString()
And obviously it is easy to refactor
Is not a good idea access to the property name.
With reflection you are breaking OOP principles.
getTrunk() not allways need to access a trunk property
You can have a
private Trunk trunk;//remember to change the TRUNK_FIELD
public static final TRUNK_FIELD = "trunk"
but... try to avoid this solution.
Related
There a json object to be sent to the server, which contains a field:
{"sName":"something"}
In my request model,I declare a var with the same name:
String sName;
But I got null when I receive in the Controller.
I change the field name to lower-case(sname) or add JsonProperty(value="sName") annotation,it work. So where is the problem?
Controller
public ResponseEntity<JSONObject> getComprehensiveInquiryCp(#Validated #RequestBody ComprehensiveInquiryRequestModel body) {
Map<String, Object> content;
JSONObject result = new JSONObject();
String sLicense = body.getSLicense();
...
}
ComprehensiveInquiryRequestModel
#Data
public class ComprehensiveInquiryRequestModel {
...
//#JsonProperty(value = "sLicense")
private String sLicense;
...
}
From top of my head: if you have accessors in that bean, then I think jackson prefers to use them if they exist. And/or Jackson prefers accessors for private fields. As you noticed you can alter that behaviour with Jakson configuration (for example via annotions).
Try:
1. to debug, remove accessor methods and make field public. If that works then change the field back to private and make sure accessor methods are named correctly.
Also single charater prefixes are not a good practise. They can be problematic and confusing. Prefixes in general are lazy and un-Clean Code(tm) practise.
It is important that your setters (and getters) are present and actually conform to the Java naming conventions. A json property named "myFirstName" usually requires a public setter "setMyFirstName(...)" for example. So "sName" needs "setSName()", I guess.
Sure sounds like the naming convnetion might be at fault here.
Let's say I have a generated Entity like this:
public class QCandidate extends EntityPathBase<Candidate> {
public final com.avisto.candisearch.persistance.model.enums.QAvailabilityEnum availability;
public final DatePath<java.util.Date> birthDate = createDate("birthDate", java.util.Date.class);
public final NumberPath<Long> cvId= createNumber("cvId", Long.class);
public final StringPath city = createString("city");
}
My input values are the fields names ("availability","birthDate","cvId"...) and a string value that I should use to perform a 'like' with all the fields.
I want to build a query starting from the field names that:
casts Dates and Numbers to strings and lowercases them
if the field is an EntityPathBase (like availability) extracts the id and then again casts to lowercased string
Something like:
lower(cast(C.cvId as varchar(255))) like 'value'
for each field.
I can do this usign querydsl-sql module, but I want to achieve it using only the jpa module.
I'm not interested in the mechanism of creating the FULL 'where' clause (I know I have to use the BooleanBuilder, or at least, this is what I do in the sql version).
What I want to know is how to create the individual 'where' conditions basing on the field type.
I'm trying to use a PathBuilder but it seems that to use methods like "getString or getBoolean" you already have to know the type of the field that you are trying to extract. In my case, since I start just from the field name, I can't use these methods and I don't know how to identify the type of each field starting from the field name, so I'm stuck.
May be a bit ugly, but workable suggestion.
Note, that the number of field types that PathBuilder accepts is quite limited.
You definitely can find the field class from field name (using reflection or by maintaining a member map updated with each field).
Just implement handling for each specific type.
This can be ugly bunch of if..else or, for more elegant solution, create Map of type handlers [class->handler], each handler implements interface method to handle specific type.
Pseudocode:
//building query
for each field
Class fieldClass = findFieldClas(.., field) //use reflection or map
PathHandler handler = handlers.get(fieldClass)
handler.process( ...)
//type handler interface
public interface Handler{
public xx process(? extends DataPathBase);
}
//specific type handler implementation
public class BooleanHandler implements Handler{
public xx process(? extends DataPathBase path){
BooleanPath bPath = (BooleanPath)path;
...
}
//intitialize handlers map singleton or a factory in advance
handlers.put(BooleanPath.class, new BooleanHandler());
...
Note this is a generic solution if you have many classes. If you have only one specific class, you can just create a permanent map of fieldName->Handler and avoid lookup for the field class.
Again, this is by no means a pretty solution, but should work.
I need to create a method that takes in argument any attribute of any class. But i dont want it to be of type String, to avoid refactoring problems while renaming an attribute and to get the errors in Markers Tab of eclipse, and not while running my application.
Having a class Person :
public class Person {
private String name;
// other attributes...
// getters and setters...
}
Now the needed method :
void getAnAttributeOfAClass( <which_type_or_class_here?> attr_as_arg){
// Now I need to get the name of attribute that would be of class Strin...
}
Is there a function or a method, by which we can specify an attribute?
For example :
Person.class.name
Would it be of class Property ?
EDIT
More exactly (#Smallhacker answer helped me), I need to verify at compile time if the argument is really an attribute of the specified class.
Person.class.name // no compile time error
Person.class.nameXXX // compile time error
The closest to what you want is Reflection API's Field or JavaBeans Introspector API's PropertyDescriptor.
But usually things like that are not needed in Java projects because there are libraries which handle these concerns.
You could pass a Class object along with a String name, then let your method use Introspector internally to read that property.
Not sure I understand you well, but there is a class java.lang.reflect.Field, that has a method getName() that would give your the name of the field.
In your example, to get field name, you would do: Person.class.getDeclaredField("name").
EDIT: to get the value of a field in an object, you would do: field.get(obj);
OK, let's say You have the following variables:
Person person = ...; // initialized with some Person
Field nameField = Person.class.getDeclaredField("name");
Now to get the name of person, you would do:
String personName = (String)nameField.get(person);
Actually, this would throw an exception because name is a private field. You can however bypass the protection by doing:
nameField.setAccessible(true);
Unfortunately, Java lacks an ability to reference member variables in a way that can be analyzed at compile time.
There may be some kind of library to simplify this somewhat, but it wouldn't provide a full solution due to limitations in the language itself.
Maybe java generics can help you with this.
You can do something like:
class YourClass<E> {
void getAnAttributeOfAClass(E attr_as_arg){
// some code
}
}
someVariable = new YourClass<Person>();
someVariable.getAnAtributeOfAClass(someObject); //this will not compile if someObject is not an instance of Person
But I still don't know what you want to do exactly inside the method.
Suppose I have a class called Person:
public class Person
{
private String name, ID, location;
}
Instead of writing individual accessors for name, ID, and location, is there anyway to do something like this:
public String get%s
{
return this.%s
}
Where "%s" stands as a sort of place holder for the name of the String which I want to "get". This way, for the sake of convenience and efficiency, I will be able to write one method to access all three Strings. Thanks.
Java doesn't have this feature, but if you use Eclipse, you can automatically generate setters and getters for your class members.
I'm not aware of anything like that in Java, but Groovy does almost exactly that, exposes JavaBean get/sets as if they were just public.
class Person {
private String name
private String location
private String id
String toString() {
"Person( name: $name, location: $location, id: $id )"
}
}
def bob = new Person()
bob.name = "bob";
bob.location = "seattle"
def robert = new Person( name: "robert", location: "seattle" )
println "bob is $bob, robert is $robert"
It works with your old school JavaBeans as well. Give groovy a try, it has stuff that Java should have had all along and it interoperates with Java with no impedance mismatch (an ints and int, a BigDecimal is a BigDecimal, I can get all all my Jars, the list goes on and on).
By-in-large, JavaBean get and set method generation has become the function of the IDEs. In Eclipse, you set up your member variables, then select "Source, Generate Getters and Setters..." and select which fields you want to expose an how.
Have a look at Project Lombok, which generates getters and setters at compile time.
Quotes from the feature overview:
#Getter / #Setter:
Never write public int getFoo() {return foo;} again.
#ToString:
No need to start a debugger to see your fields: Just let lombok generate a toString for you!
#Data:
All together now: A shortcut for #ToString, #EqualsAndHashCode, #Getter on all fields, and #Setter on all non-final fields, and #RequiredArgsConstructor!
Your Person class would seems like the perfect target for an #Data annotation.
I use Lombok together with JSF and Hibernate in NetBeans and it works like a charm.
You cannot do such a thing in Java.
You can use Groovy (works perfectly well in a Java project mixed with Java classes), you won't have to worry about declaring getters and setters anymore.
I have data model classes that contain private fields which are meant to be read-only (via a getter function). These fields are set by my JPA persistence provider (eclipselink) during normal operation, using the contents of the database. For unit tests, I want to set them to fake values from a mockup of the persistence layer. How can I do that? How does eclipselink set these values, anyway?
Simplified example:
#Entity
class MyEntity
{
#Id
private Integer _ix;
public Integer ixGet()
{
return this._ix;
}
}
Can you just Mock the Entity itself, providing your own implemenations of the getters?
You could create an anonymous extension in your mock persistence layer:
MyEntity x = new MyEntity() {
public Integer ixGet() { return new Integer(88); }
};
You need to use the Reflection API. Use Class.getField() to get the field, then call setAccessable(true) on that field so that you may write to it, even though it is private, and finally you may call set() on it to write a new value.
For example:
public class A {
private int i;
}
You want to set the field 'i' to 3, even though it is private:
void forceSetInt(Object o, String fieldName, int value) {
Class<?> clazz = o.getClass();
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(o, value);
}
There are a number of exceptions that you will need to handle.
You can use a test library like Mockito to access objects internal state in read and write mode. For example with Mockito use:
//read
Integer i = Whitebox.getInternalState(myEntity,"_ix")
//Write
Whitebox.setInternalState(myEntity,"_ix", 123)
You can use a mocking framework like powermock to by pass encapsulation. In powermock you'd use Whitebox.setInternalState(..) to set a private member.
A less invasive method would be to mock the getter method. Whether this is feasible would depend on what else depends on the internal state but if it is enough, it's the cleaner solution.
Some methods I've used in the past:
Make _ix protected, create a subclass where you implement a setter
Make a constructor taking the value for _ix as a parameter
Use reflection
Another option, if you really hate to make things public, is to create a subclass for testing, and provide public access there.
You have a few options:
Create stubs to replace your entity (extract an interface first)
Use Reflection
Add a public setter for testing
Keep your tests within the package and use a default scope
For a bunch of useful techniques, have a look at Michael Feather's book, Working Effectively With Legacy Code
You can add constructor with parameter for your read-only variable. Don't forget to add a default (zero parameter) constructor.
#Entity
class MyEntity
{
#Id
private Integer _ix;
public MyEntity(Integer ix) {
_ix = ix;
}
public MyEntity() {
/*
* Default constructor
*/
}
public Integer ixGet()
{
return this._ix;
}
}
The constructor is a best way I think. If this entity has to be really readonly (not allowed to create new instances in production code at all) you can make constructor with package access and use it only within the tests. And there is a possibility that even if you make your default constructor private or with package access, your persistance provider still be able to work with such entity, but not sure though - check with eclipselink docs.