Is there some similar feature in Java as 'attributes' in Delphi ?
Example explanation of Attributes in Delphi:
http://www.malcolmgroves.com/blog/?p=476
Att.
From that article, you're looking for Java Annotations. They let you do things like:
#SomeInfo(author = "Bob", year = 1993)
class Foo {
#SomeInfo(author = "me", somethingElse = "abcdefg")
private int x = 5;
#SomeInfo(author = "Fred", column = "order")
public int getX() {
return x;
}
}
where #SomeInfo is an annotation. They can be applied to classes, fields, and methods, and they carry metadata about the thing they annotate, which can be read at runtime if they have the appropriate retention. E.g:
#Retention(RetentionPolicy.RUNTIME)
#interface SomeInfo {
String author();
int year() default -1;
String column() default "";
String somethingElse() default "";
}
class Main {
public static void main(String[] args) throws Exception {
List<AnnotatedElement> annotatedElements =
new ArrayList<AnnotatedElement>();
annotatedElements.add(Foo.class);
annotatedElements.add(Foo.class.getDeclaredField("x"));
annotatedElements.add(Foo.class.getDeclaredMethod("getX"));
for (AnnotatedElement annotatedElement : annotatedElements) {
System.out.println("Author of {" + annotatedElement + "} = " +
annotatedElement.getAnnotation(SomeInfo.class).author());
}
}
}
It looks like an attributes is a way to store diffrent variable together.
That is what OOP is (Very)generally about (And so is JAVA), creating classes that represents entities. Those entities are basically made of different variables (or attributes)
This example from your link and a comparison to JAVA will make it clearer:
MyAttribute = class(TCustomAttribute)
private
FName: string;
FAge: Integer;
public
constructor Create(const Name : string; Age : Integer);
property Name : string read FName write FName;
property Age : Integer read FAge write FAge;
end;
Is just like a little class in JAVA:
public class Customer()
{
String Fname;
int FAge;
public Customer()
{
/*constructor code*/
}
}
and creating the class from your example:
TMyClass = class
public
[MyAttribute('Malcolm', 39)]
Is just like creating a new person object:
Customer[] cust1= new Customer['Malcolm', 39]
Related
I have a config file with key value pair as
language = "IN"
and i have multiple page object enum files with name as
PageObject_US,PageObject_UK,PageObject_IN
every page object enum file has constants that can be accessed using for example
PageObjects_US.String.lable
but what i want to achieve is a way to create something like below
take the parameter from config file store it in a string
like String language = "IN"
Then concatenate using "PageObjects_" + language to get (PageObjects_IN)
so that the returned value can be used to fetch the constants from PageObjects_IN.String.label.
following is the code block:
if(!ENV.equalsIgnoreCase("development") && VALIDATION.equalsIgnoreCase("yes")) {
Elements.ByTitle(webDriver,PageObjects_IN.GREAT.label);
Elements.ByID(webDriver, PageObjects_IN.COUNTER.label);
}
In the above i want to use enum file PageObjects_IN at run time as i have many enum files
below is the enum
public enum PageObjects_IN {
// Text
GREAT("great"),
COUNTER("counter");
public final String lable;
PageObjects_IN(final String lable) {
this.lable = lable;
}
}
This is possible (using reflection) but strongly not recommended as it eliminates the efficiency of Java language constructs.
Not recommended way
Say you have a package click.webelement.cucumber.po where you store
public enum PO_EN {
GREAT("great_en"),
COUNTER("counter_en");
public final String label;
PO_EN(String label){
this.label = label;
}
}
and
public enum PO_IN {
GREAT("great_in"),
COUNTER("counter_in");
public final String label;
PO_IN(String label){
this.label = label;
}
}
Then to take a value you can do something like this:
String lang = "EN";
// Take class
Class clazz = Class.forName("click.webelement.cucumber.po.PO_" + lang);
// Find an object that represent enum constant
Object val = Arrays
.stream(clazz.getEnumConstants()).filter(o -> "GREAT".equals(o.toString()))
.findAny()
.get();
// Take field value for that object
Field f = clazz.getField("label");
System.out.println(f.get(val));
This is error-prone approach and you would not have benefit from compile phase.
Recommended approach - 1
Instead of having enum use classes.
public abstract class PO {
public abstract String great();
public abstract String counter();
}
and
public class PO_EN extends PO{
#Override
public String great() {
return "great_en";
}
#Override
public String counter() {
return "counter_en";
}
}
and
public class PO_IN extends PO{
#Override
public String great() {
return "great_in";
}
#Override
public String counter() {
return "counter_in";
}
}
so your test would be much simpler
String lang = "EN";
Class clazz = Class.forName("click.webelement.cucumber.po.PO_" + lang);
PO val = (PO) clazz.getDeclaredConstructor().newInstance();
System.out.println(val.great());
Recommended approach - 2
You can utilize PageFactory harness for your page objects and use this lib to parametrize your locators, like (if you use test ng):
#DataProvider(name = "languages")
Object[][] dataProvider(){
return new Object[][]{
{"en", "great_en", "counter_en"},
{"in", "great_in", "counter_in"}
};
}
#Test(dataProvider = "languages")
public void testPage(String language, String great, String counter){
DefaultParameterProvider
.properties
.set(Map.of("p.great", great, "p.counter", counter));
MyPage myPage = new MyPage(driver);
...
}
Where your page would be like this:
public class MyPage extends PageObjectParameterized {
#FindByParameterized(xpath = "//button[#name='{wec:p.great}']")
WebElement great;
#FindByParameterized(xpath = "//label[text()='{wec:p.counter}']")
WebElement counter;
#FindBy(xpath = "//input")
WebElement input;
public MyPage(SearchContext searchContext) {
super(searchContext);
}
}
I have a class that overrides ArrayList like:
public class SkmeList extends ArrayList<SkmeStatement> {
private static final long serialVersionUID = 1L;
private int skmeMajor = 0;
private int skmeMinor = 0;
private String skmeTable = null;
public void setTable(String table) {
System.out.println("Set Table: " + table);
skmeTable = table;
}
public String getTable() {
return skmeTable;
}
public void setMajor(int major) {
System.out.println("SetMajor: " + major);
skmeMajor = major;
}
public int getMajor() {
return skmeMajor;
}
public void setMinor(int minor) {
System.out.println("SetMinor: " + minor);
skmeMinor = minor;
}
public int getMinor() {
return skmeMinor;
}
}
when I attempt to write this class to a file or even a string using jackson I can only see the list contents, I do not see any of class specific attributes like Major or minor in the string/file? I treat this class just like any other java class. Is there something that is different with lists in jackson object mapper?
public void WriteJson(SkmeList statements) {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final ObjectMapper mapper = new ObjectMapper();
try {
mapper.writeValue(out, statements);
final byte[] data = out.toByteArray();
System.out.println(new String(data));
}
catch (IOException ioe) {
System.out.println("Foo");
}
}
A List has elements and no further non-element data. If you need more data, you need something that's more than a List.
The user of your class already has to treat it specially if they care about any of the extra fields you've added.
In favoring composition over inheritance, here's how I'd suggest this class could look like.
public class SkmeList {
private final int major;
private final int minor;
private final String table;
private final List<SkmeStatement> statements;
// ctor, getters, hashCode, equals and toString omitted
}
With more context on what Skme means, we could make the naming even clearer.
To make it easier to reason about, the class should be immutable, to make it safe for use in a Collection it should have hashCode() and equals(), and a toString() in case it ever gets printed/logged/debugged around.
If you don't feel like implementing all the omitted methods, consider AutoValue: you specify the getters and a factory method, the rest is generated for you.
For the user of your class, it's almost the same:
SkmeList list = ...
for (SkmeStatement stmt : list) {
...
now becomes
SkmeList list = ...
for (SkmeStatement stmt : list.getStatements()) {
...
I have a class X with maybe 100 String in it and I want to do a function that mock an object of this class for all the setters which begins by "setTop".
For the moment I did this :
public void setFtoMethods(Class aClass){
Methods[] methods = aClass.getMethods();
for(Method method : methods){
if(method.getName().startsWith("setTop")){
method.invoke ....
}
}
}
And I don't know how to do now and I'm not pretty sure I can fill all these setters like that. In my environment I can't use frameworks and I'm in Java 6.
You CANNOT fill the setters because they are methods (functionallities), not values itselfs. But...
You CAN fill the value of the attributes (fields) of the class that corresponds to the getter.
Imagine you have a class:
class Example {
String name;
int topOne;
int topTwo;
int popTwo; // POP!!!
int topThree;
}
Taking:
this answer as model
your needs (getTopXXX will correspond to field topXXX)
You can get only needed fields with reflection in this way:
public static void main(String[] args) {
inspect(Example.class);
}
public static <T> void inspect(Class<T> klazz) {
Field[] fields = klazz.getDeclaredFields();
for (Field field : fields) {
if (field.getName().startsWith("top")) {
// get ONLY fields starting with top
System.out.printf("%s %s %s%n",
Modifier.toString(field.getModifiers()),
field.getType().getSimpleName(),
field.getName()
);
}
}
}
OUTPUT:
int topOne
int topTwo
int topThree
Now, do whatever you need inside the if (field.getName().startsWith("top")) { instead of a System.out.
Let's say I have a class named Person and its constructor had variables like name, age, hairColor and so on. If I had a function that receives a string that should match one of the class's variables, how could I check if that class actually had that variable and how could I go about modifying it? For example:
public class Person {
public String name;
public int age;
public String hairColor;
public Person() {
name = "Bryce";
age = 21;
hairColor = "brown";
}
public void changeHairColor(String variable, String color) {
if (/*this class contains the variable passed as an argument to this method*/) {
// Person[variable] = color
}
}
}
I'm a python dev, mostly, so the method changeHairColor has some pseudo-python in it. I want to be able to edit the variable in a similar way you could edit variables inside of dictionaries with Python:
person = {
"name": "Bryce",
"age": 21,
"hairColor": "brown"
}
def changeHairColor(variable, color):
person[variable] = color
If that is at all possible.
The only way to do it in Java is to use Java Reflection API:
public class Test {
public String name;
public int age;
public String hairColor;
public void setProperty(String property, Object value) {
try {
Field declaredField = this.getClass().getDeclaredField(property);
switch (declaredField.getAnnotatedType().getType().getTypeName()) {
case "java.lang.String":
declaredField.set(this, value);
break;
// handle other types
}
} catch (NoSuchFieldException e) {
// handle exception
} catch (IllegalAccessException e) {
// handle exception
}
}
public static void main(String[] args) {
Test test = new Test();
test.setProperty("name", "Bob");
System.out.println(test.name);
}
}
I would not solve this with reflection. If your PlayerCharacter has an enumerable set of attributes, I would model these as a Java enum and store the attribute values within the PlayerCharacter object in an EnumMap:
import java.util.EnumMap;
public class PlayerCharacter {
public enum Attribute {
AGILITY,
DEXTERITY,
/* etc ...*/
VITALITY
}
private EnumMap<Attribute, Integer> attributes = new EnumMap<>(Attribute.class);
public PlayerCharacter() {
// initialize each attribute with a default value (0 in this example)
for (Attribute a : Attribute.values()) {
attributes.put(a, new Integer(0));
}
}
public int getValue(Attribute attribute) {
return attributes.get(attribute);
}
public void levelUp(Attribute attribute, int amount) {
attributes.put(attribute, attributes.get(attribute) + amount);
}
}
The biggest benefit of using an enum instead of plain old String (+reflection), is that this way you get compile-time type safety for the code that's using your PlayerCharacter.
Using Reflection API, you can access the methods and properties on an object at run time. The other answer describes its usage. But I don't recommend reflections for your problem. How about the following:
public void changeHairColor(String variable, String color) {
if("name".equalsIgnoreCase(variable))
this.name = color;
else if("age".equalsIgnoreCase(variable))
this.age = Integer.parseInt(color);
else if("color".equalsIgnoreCase(variable))
this.color = color;
else
throw new Exception ("error - property not available");
}
}
Note, your existing method name 'changeHairColor' doesn't make sense in the context. It should be someething like 'changeProperty' because you are not just changing the 'color', you are changing any available property with this method.
I am calling an external service that returns the following class:
package abc;
public class FirstClass {
private String name;
private String age;
private String number;
}
In my model, I have defined a class with same structure:
package xyz;
public class FirstClass {
private String name;
private String age;
private String number;
}
I want to copy data from the abc.FirstClass object to xyz.FirstClass object. I don't want to map the data field by field. I think it can be done by dozer - are there any easier methods to do it?
You could use PropertyUtils#copyProperties:
Copy property values from the "origin" bean to the "destination" bean for all cases where the property names are the same (even though the actual getter and setter methods might have been customized via BeanInfo classes).
Example:
abc.FirstClass src = new abc.FirstClass();
xyz.FirstClass dest = new xyz.FirstClass();
PropertyUtils.copyProperties(dest, src);
The reason you cannot copy the entire class object is because each class object has unique hashcode. Even if your class has same attribute.
I know it is a pain but mapping by field is the only way to go.
You can try use spring beans to help map the fields.
Java Reflection is the way to go.
From The Java™ Tutorials
A field is a class, interface, or enum with an associated value. Methods in the java.lang.reflect.Field class can retrieve information about the field, such as its name, type, modifiers, and annotations. There are also methods which enable dynamic access and modification of the value of the field
Try something like this:
StackOverflow.abc.firstClass abc = new StackOverflow.abc.firstClass();
StackOverflow.xyz.firstClass xyz = new StackOverflow.xyz.firstClass();
Class<? extends StackOverflow.xyz.firstClass> xyzClass = xyz.getClass();
Field[] fields = abc.getClass().getDeclaredFields();
for (Field abcField : fields) {
abcField.setAccessible(true); //To access private fields
try {
Field xyzField = xyzClass.getDeclaredField(abcField.getName());
xyzField.setAccessible(true);
xyzField.set(xyz, abcField.get(abc));
} catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
You can check out this tutorial from Oracle for more details. Or, specifically this page from the tutorial.
You can using reflection
this does the job and shows it:
public class FirstClass {
private String name;
private String age;
private String number;
public FirstClass(String _name, String _age, String _number)
{
name=_name; age=_age; number=_number;
}
}
public class CloneClass {
private String name;
private String age;
private String number;
public CloneClass(String _name, String _age, String _number)
{
name=_name; age=_age; number=_number;
}
public void show()
{
System.out.println("NAME="+name+" AGE="+age+" NUMBER="+number);
}
}
FirstClass A=new FirstClass("Jules","44","123A4535");
CloneClass B=new CloneClass("","","");
Class class1=A.getClass();
Class class2=B.getClass();
// all fields from A
Field[] fields_A = class1.getDeclaredFields();
Field[] fields_B = class2.getDeclaredFields();
for (int k=0;k<fields_A.length;k++)
{
Field one_field=fields_A[k];
// Name of field in source
String name_of_field=one_field.getName();
if (name_of_field.equals("this$0")) continue; // Not this !
// Search if it exists in destination
for (int z=0;z<fields_B.length;z++)
{
Field field_destination=fields_B[k];
String name_of_field2=field_destination.getName();
if (name_of_field.equals(name_of_field2))
// TODO
// You should also verify the type !
{
try
{
// To read private var
one_field.setAccessible(true);
field_destination.setAccessible(true);
Object value=one_field.get( A);
field_destination.set(B, value);
}
catch (Exception ex) {System.err.println(ex);}
}
}
} // for (int k=0;k<fields_A.length;k++)
B.show();