I have a SimplePojo class and I would like to retrieve parameter modifier at run-time using reflection.
However, it does not seem to work ... SSCEE
public final class SimplePojo {
private final String name;
private final int age;
public SimplePojo(String name, final int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setAge(int age) {
}
public int getAge() {
return age;
}
}
And this is how I try to check if parameter modifier is FINAL
for (Class<?> paramClazz : method.getParameterTypes()) {
if (!Modifier.isFinal(paramClazz.getModifiers())) {
throw new ConstraintViolationException(
String.format("Parameters of method '%s' in '%s' must be declared as 'final'",
method.getName(),
point.getTarget().getClass().getCanonicalName()
)
);
}
}
EDIT:
//are all constructors params final
for (Constructor constructor : clazz.getConstructors()) {
for (Class<?> constructorParam : constructor.getParameterTypes()) {
Log.e(TAG, "constructorParam:" + constructorParam.getName() + ", mod: " + constructorParam.getModifiers());
if (!Modifier.isFinal(constructorParam.getModifiers())) {
throw new ConstraintViolationException(
String.format("Constructor parameters in '%s' annotated with '%s'" +
" must be declared as 'final'",
clazz.getCanonicalName(),
Inmutable.class.getSimpleName()
)
);
}
}
}
and output:
constructorParam:java.lang.String, mod: 17
constructorParam:int, mod: 1041
Assuming you are using Java 8, you can use the Executable.getParameters() method to get the formal parameters of a Method.
This returns an array of Parameter instances, on which you can invoke Parameter.getModifiers().
I don't believe there is a standard pre-Java 8 solution.
getModifiers returns a set of flags.
Try this:
final int FINAL = 0x0010;
if (!(paramClazz.getModifiers() & FINAL)) {
throw new ...
Related
In python we can compare object by attributes by this method:
def __eq__(self, other):
return self.__dict__ == other.__dict__
It allows us to compare objects by their attributes no matter how many attributes they have.
Is something like this possible in Java?
I tried to compare objects in this way, but you have to compare them by every attribute. Is it possible to do it similarly to Python eq method above?
public class MyClass {
public MyClass(attr1, attr2, attr3, attr4) {
this.attr1 = attr1;
this.attr2 = attr2;
this.attr3 = attr3;
this.attr4 = attr4;
}
public boolean equals(Object object2) {
return object2 instanceof MyClass &&
attr1.equals(((MyClass)object2).attr1)&&......;
}
}
If you want to write general method that handles every type, you need to use reflection. If you just want to do this with a type or two, then I suggest you override the equals method for each individual type i.e. hardcoding it.
Here's how you write a generic method that accepts every single type and compares the fields for equality:
private static <T> boolean allFieldsEqual(T a, T b) throws IllegalAccessException {
Class<?> clazz = a.getClass();
Field[] fields = clazz.getFields();
for (Field field : fields) {
if (!field.get(a).equals(field.get(b))) {
return false;
}
}
return true;
}
The logic is pretty self-explanatory. Here is the usage:
Student s1 = new Student("Tom", 1);
Student s2 = new Student("Tom", 1);
try {
System.out.println(allFieldsEqual(s1, s2));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Student is defined as:
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
Here is my Code :
public class SearchByLambda {
private Map<String,Consumer<Person>> searchCritertiaHolder = new HashMap<String,Consumer<Person>>();
private static final String AGED = "aged";
public SearchByLambda(){
searchCritertiaHolder.put(AGED, (Person p)-> {p.filterAgedPerson(p);} );
}
private Consumer<Person> getFilter(String personType){
return searchCritertiaHolder.get(personType);
}
public static void main(String[] args) {
SearchByLambda searchUsage = new SearchByLambda();
Person p = new Person(59,"shailesh");
Person p1 = new Person(58,"ganesh");
searchUsage.getFilter(AGED).accept(p);
searchUsage.getFilter(AGED).accept(p1);
Person.printAgedPersons();
}
}
class Person{
private static List<Person> agedPersons = new ArrayList<>();
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(int age,String name){
this.age = age;
this.name = name;
}
public void filterAgedPerson(Person person){
if(person.getAge() > 58){
agedPersons.add(person);
}
}
public static void printAgedPersons(){
for(Person person : agedPersons){
System.out.println(person.getName());
}
}
}
When I replace following Lambda expression
searchCritertiaHolder.put(AGED, (Person p)-> {p.filterAgedPerson(p);});
with
searchCritertiaHolder.put(AGED, Person::filterAgedPerson);
it gives me compilation error. I am using java 8 and and compiling through eclipse. Why is this so? Why cannot I assign method reference for instance method of any arbitrary object to consumer functional interface?
Your definition of filterAgedPerson takes a Person as an argument, even though it is not a static method. It doesn't need to, and it shouldn't if you want to use it as a Consumer<Person>. What you are ending up with is something compatible with BiConsumer<Person, Person>.
It might help to think of it this way: method references to non-static methods always take an "extra" argument which is used as this.
The easiest way for you to fix this with your current code structure is to modify the filterAgedPerson method to not take a Person as an argument
public void filterAgedPerson() {
if (this.getAge() > 58) {
agedPersons.add(person);
}
}
As an aside, you might want to also consider making your filters Predicate<Person> instead of Consumer<Person> and moving the results handling elsewhere. This will give you more flexibility as things get more complicated.
Here is my field.java class. Ive got the public Field(String name, int number) defined here.
public class Field
{
String name;
int number;
public Field(String name, int number){
this.name = name;
this.number = number;
}
public String getName() {
return name;
}
public int getNumber() {
return number;
}
#Override
public String toString() {
return "Field{" + "name=" + name + ", number=" + number + '}';
}
}
Here is my Player.java class, Im getting an error on my Field currentField = new Field(); - it says that my Field is not defined as a constructor in my Field.java class
public class Player
{
private String name;
private int pos;
Field currentField = new Field();
public Player()
{
}
}
Anyone got a suggestion on why Im throwing errors?
You have provided a parameterized constructor in your class
public Field(String name, int number){
this.name = name;
this.number = number;
}
And hence no default (no-arg) constructor is provided when you define a parametrized constructor.
So when you are trying to create an instance using Field currentField = new Field();, it cannot compile since there is no matching constructor.
Solutions you can try:
1.
Add a no-arg constructor to your class :
public Field()
{
}
Or
2.
While creating an instance, pass values to constrcutor :
Field currentField = new Field("abc", 123);
Yes because your class receiving two arguments name and number and you are trying to create instance of it without passing them.
Either you can pass them
Field currentField = new Field("test", 1); // for ex :
or create a default no arg constructor to your Field class.
/** default no arg constructor **/
public Field(){
// TODO : when there is no param
}
Field currentField = new Field();
You are not passing any arguments to the constructor. You will either need to provide a name and number as parameters, or define a default constructor :
public Field(String name, int number){
this.name = name;
this.number = number;
}
Field currentField = new Field("fieldName", 1);
or
public Field(){
this.name = "";
this.number = 0;
}
Field currentField = new Field();
In every Java class there is a default constructor, if you add any other constructor it will override the default constructor. So to make your code work you have to add no argument constructor.
public Field(){
}
This question already has answers here:
Java error: Implicit super constructor is undefined for default constructor
(12 answers)
Closed 8 years ago.
I'm trying to extend my Vehicle class to a HumanPowered class -- Has a field for calories per hour. This is my first time trying to extend a class so I'm a bit confused here.
class Vehicle
{
String description;
String idNumber;
int numWheels;
public Vehicle(String aDescription, String aIdNumber, int aNumWheels)
{
description = aDescription;
idNumber = aIdNumber;
numWheels = aNumWheels;
}
void setDescription (String aDescription)
{
description = aDescription;
}
void setIdNumber (String aIdNumber)
{
idNumber = aIdNumber;
}
void setNumWheels (int aNumWheels)
{
numWheels = aNumWheels;
}
public String getDescription()
{
return description;
}
public String getIdNumber()
{
return idNumber;
}
public int getNumWheels()
{
return numWheels;
}
public String toString()
{
String result= String.format("ID: %s Description: %s Wheels: %d",idNumber,description,numWheels);
return result;
}
}
class humanPowered extends Vehicle
{
int calories;
public humanPowered(String aDescription, String aIdNumber, int aNumWheels, int aCalories) //Error here
{
description = aDescription;
idNumber = aIdNumber;
numWheels = aNumWheels;
calories = aCalories;
}
void setCalories (int aCalories)
{
calories = aCalories;
}
public int getCalories()
{
return calories;
}
public String toString()
{
String result= String.format("ID: %s Description: %s Wheels: %d Calories per Hour: %d",idNumber,description,numWheels, calories);
return result;
}
}
I'm getting an error marked above on my constructor for my humanPowered class saying "Implicit super constructor Vehicle() is undefined. Must explicitly invoke another constructor." I can't figure out where I'm going wrong here. Thanks for any and all help!
Vehicle don't have default constructor hence you have to call its constructor form humanPowered class passing required arguments at the first line of its constructor.
public humanPowered(String aDescription, String aIdNumber, int aNumWheels, int aCalories)
{
super(aDescription,aIdNumber,aNumWheels);
...//other code
}
Points to remember:
Every class have default constructor that is no-argument constructor
If class creates a constructor passing arguments then by default constructor is not created
Each constructor by default calls default constructor of its super-class
I am writing a deserializer method, which looks like so:
public <T> T deserialize(Object[] result, String[] fields, Class<T> type);
So basically I will be passed in a result array of data which is all objects, and a class type T which I need to convert the data in the array to the types in the given class, and create a new class of type T and return it. The String[] fields is the field names corresponding to the data in Object[] result. The field names will correspond to the Class T.
The casting will need to use reflection of the given class to find out the type of each field.
eg.
result = ["Mike", "London", 28];
fields = ["name", "location", "age" ];
Class T =
public class GivenClass{
private String name;
private String location;
private Integer age;
public GivenClass(String name, String location, Integer age){
this.name = name;
this.location = location;
this.age = age;
}
}
Class implementation
static class GivenClass {
private String name;
private String location;
private Integer age;
public GivenClass(String name, String location, Integer age) {
this.name = name;
this.location = location;
this.age = age;
}
public GivenClass(Map<String, Object> data) throws Exception {
for (Field f : GivenClass.class.getDeclaredFields())
f.set(this, data.get(f.getName()));
}
public Map<String, Object> serialize() throws Exception {
Map<String, Object> fields = new HashMap<String, Object>();
for (Field f : GivenClass.class.getDeclaredFields())
fields.put(f.getName(), f.get(this));
return fields;
}
#Override
public String toString() {
return "age=" + age + ", location=" + location + ", name=" + name;
}
}
Example:
public static void main(String[] args) throws Exception {
GivenClass o1 = new GivenClass("Mike", "London", 28);
Map<String, Object> serialized = o1.serialize();
GivenClass o2 = new GivenClass(serialized);
System.out.println(o2.toString());
}
Output:
age=28, location=London, name=Mike
You need to do the conversion yourself. Reflections doesn't convert (it will only check the type of an object is already correct)
Reflections won't give you the names of method/constructor parameters. (You can get them from the debug byte code but that's a real pain)
The approach I take is to use the convention that the constructor parameters are in the same order as the fields. You will also want to assume the type of constructor parameters and field types match. ;)
I would also use primitives instead of wrappers whenever possible. Use int unless you want null to be a valid option. If this is the case you should think about how you want to represent this. For text I usually use empty strings or blank field for null or NaN depending on the context.
The problem with this, is that in Java it's unable to fetch the parameter names of a constructor.
For this particular example, you'll need a default constructor, with which you could create an empty object.
public GivenClass() {
super();
}
Then you could use reflection to get the fields of the class, and then set the appropriate value for them.
But I think it would be much easier to annotate your constructor, and then fetch the annotation informations in your deserialize method. In this case you won't need to fetch the fields and create an empty constructor.
Example:
You need to create a annotation like this:
#Target({ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface Property
{
String value();
}
And then you can use it in your constructor like this:
public GivenClass(#Property("name") String name, #Property("location") String location, #Property("age") Integer age) {
// ...
}
As Peter Lawrey says, casting does not convert a string into an integer.
If your bean follows the standard bean conventions (ie you have getters & setters), then you can use BeanUtils. BeanUtils does some standard conversions, and you can add more by adding a Convertor.
See the following example:
import org.apache.commons.beanutils.BeanUtils;
public class BeanUtilsTest {
public static class Obj {
private int number;
private String string;
public void setNumber(int number) {
this.number = number;
}
public void setString(String string) {
this.string = string;
}
public String toString() {
return "number=" + number + " string=" + string;
}
}
public static void main(String args[]) throws Exception {
String[] values = new String[] { "1", "two" };
String[] properties = new String[] { "number", "string" };
Obj obj = new Obj();
for (int i = 0; i < properties.length; i++) {
BeanUtils.setProperty(obj, properties[i], values[i]);
}
System.out.println("obj=" + obj);
}
}
This produces as output:
obj=number=1 string=two
Note that the above example has only setters, but still works.