Is an enum able to store references to a getter method, using a Supplier?
To be use like that :
String value = myEnum.getValue(object)
I can't figure how to write it without compiling errors.
If I get you right then you want to do something like this:
import java.util.function.DoubleSupplier;
public class Test {
enum MathConstants {
PI(Test::getPi), E(Test::getE);
private final DoubleSupplier supply;
private MathConstants(DoubleSupplier supply) {
this.supply = supply;
}
public double getValue() {
return supply.getAsDouble();
}
}
public static void main(String... args) {
System.out.println(MathConstants.PI.getValue());
}
public static double getPi() {
return Math.PI;
}
public static double getE() {
return Math.E;
}
}
It's not very difficult if the return type for all the getters is the same. Consider the following PoJo class:
public static class MyPoJo {
final String foo, bar;
public MyPoJo(String foo, String bar) {
this.foo = foo;
this.bar = bar;
}
public String getFoo() {
return foo;
}
public String getBar() {
return bar;
}
public int getBaz() {
return 5;
}
}
Then we may have such enum:
public static enum Getters {
FOO(MyPoJo::getFoo), BAR(MyPoJo::getBar);
private final Function<MyPoJo, String> fn;
private Getters(Function<MyPoJo, String> fn) {
this.fn = fn;
}
public String getValue(MyPoJo object) {
return fn.apply(object);
}
}
And use it like this:
System.out.println(Getters.FOO.getValue(new MyPoJo("fooValue", "barValue"))); // fooValue
However it would be problematic if you want to return different types. In this case I'd suggest to use normal class with predefined instances instead of enum:
public static final class Getters<T> {
public static final Getters<String> FOO = new Getters<>(MyPoJo::getFoo);
public static final Getters<String> BAR = new Getters<>(MyPoJo::getBar);
public static final Getters<Integer> BAZ = new Getters<>(MyPoJo::getBaz);
private final Function<MyPoJo, T> fn;
private Getters(Function<MyPoJo, T> fn) {
this.fn = fn;
}
public T getValue(MyPoJo object) {
return fn.apply(object);
}
}
Usage is the same:
System.out.println(Getters.FOO.getValue(new MyPoJo("fooValue", "barValue"))); // fooValue
System.out.println(Getters.BAZ.getValue(new MyPoJo("fooValue", "barValue"))); // 5
Related
For example I have two simple as possible classes, A and B
I want to take some action on objects of B, if some specific field of A object is changed I should do one thing, If some other field is changed I should do second thing, how can I do that with Lambda?
A:
public class A {
private int someField;
private String nextField;
public A(int someField, String nextField) {
this.someField = someField;
this.nextField = nextField;
}
public int getSomeField() {
return someField;
}
public void setSomeField(int someField) {
this.someField = someField;
}
public String getNextField() {
return nextField;
}
public void setNextField(String nextField) {
this.nextField = nextField;
}
}
B:
public class B {
private String someField;
public String getSomeField() {
return someField;
}
public void setSomeField(String someField) {
this.someField = someField;
}
public B(String someField) {
this.someField = someField;
}
}
Demo:
public class Demo {
public static <T> boolean isFieldChanged(T oldValue, T newValue) {
return !Objects.equals(oldValue, newValue);
}
public static void someActionOne(B test){
return;
}
public static void someActionTwo(B test){
return;
}
public static void main(String[] args) {
A oldData = new A(35, "old");
A clientData = new A(25, "ClientData");
Consumer<B> action = null;
if (isFieldChanged(oldData.getNextField(), clientData.getNextField())) {
action = Demo::someActionOne;
} else if (isFieldChanged(oldData.getSomeField(), clientData.getSomeField())) {
action = Demo::someActionTwo;
}
List<B> mockData = new ArrayList<>(Arrays.asList(new B("test1"), new B("test2")));
mockData.forEach(b -> action.accept(b));
}
}
How can I avoid compile error in that case?
To be effectively-final, a variable must not be changed after initialization.
If you want to use different actions, just initialize them twice:
public static void main(String[] args) {
A oldData = new A(35, "old");
A clientData = new A(25, "ClientData");
List<B> mockData = new ArrayList<>(Arrays.asList(new B("test1"), new B("test2")));
if (isFieldChanged(oldData.getNextField(), clientData.getNextField())) {
mockData.forEach(Demo::someActionOne);
} else if (isFieldChanged(oldData.getSomeField(), clientData.getSomeField())) {
mockData.forEach(Demo::someActionTwo);
}
}
I understand there may be cleaner ways to store these data, I'm skipping that part for the sake of my sanity in dealing with legacy code.
I want to store an object that looks like this in DynamoDB:
#DynamoDBTable(tableName="TableName")
public class MyItem {
// DynamoDB Attributes
private String hashKey;
private String someAttribute;
private Map<String, Config> configs;
#DynamoDBHashKey(attributeName = "hash_key")
public String getHashKey() {
return this.hashKey;
}
public void setHashKey(String hashKey) {
this.hashKey = hashKey;
}
#DynamoDBAttribute(attributeName = "some_attribute")
public String getSomeAttribute() {
return this.someAttribute;
}
public void setSomeAttribute(String someAttribute ) {
this.someAttribute = someAttribute;
}
#DynamoDBAttribute(attributeName = "configs")
public Map<String, Config> getConfigs() {
return this.configs;
}
public void setConfigs(Map<String, Config> configs)
{
this.configs = configs;
}
}
With a supplemental class
#DynamoDBDocument
public class Config {
private String field;
#DynamoDBAttribute(attributeName="field")
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
}
Will this work as written?
What would the resulting entry look like in DynamoDB for the following JSON:
{
"hash_key":"123",
"some_attribute":"attribute_value",
"a_config_key" : {
"field":"field_value"
}
}
I would recommend you to implement your own converter using #DynamoDbConvertedBy (the official dynamodb-enhanced client).
Hopefully, this sample is helpful: https://stackoverflow.com/a/70602166/12869305
Since a version upgrade from an unrelated library, our DTOs have fluent setters. Basically a nice thing, but now Orika is failing to map properties
public class DebugOrikaTest {
#Test
public void simpleToFluent() {
final MapperFacade mapper = new ConfigurableMapper();
final SimpleWithBoolean a = new SimpleWithBoolean();
a.setFoo(Boolean.TRUE);
a.setBar("foobar");
final FluentWithBoolean b = new FluentWithBoolean();
// act
mapper.map(a, b);
// assert
Assertions.assertEquals("foobar", b.getBar());
Assertions.assertTrue(b.isFoo());
}
#Test
public void simpleToOther() {
final MapperFacade mapper = new ConfigurableMapper();
final SimpleWithBoolean a = new SimpleWithBoolean();
a.setFoo(Boolean.TRUE);
a.setBar("foobar");
final OtherWithBoolean b = new OtherWithBoolean();
// act
mapper.map(a, b);
// assert
Assertions.assertEquals("foobar", b.getBar());
Assertions.assertTrue(b.isFoo());
}
public static class SimpleWithBoolean {
private Boolean foo;
private String bar;
public Boolean isFoo() {
return foo;
}
public void setFoo(Boolean foo) {
this.foo = foo;
}
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
public static class FluentWithBoolean {
private Boolean foo;
private String bar;
public Boolean isFoo() {
return foo;
}
public FluentWithBoolean setFoo(Boolean foo) {
this.foo = foo;
return this;
}
public String getBar() {
return bar;
}
public FluentWithBoolean setBar(String bar) {
this.bar = bar;
return this;
}
}
public static class OtherWithBoolean {
private Boolean foo;
private String bar;
public Boolean isFoo() {
return foo;
}
public void setFoo(Boolean foo) {
this.foo = foo;
}
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
}
The simpleToOther test is green, but simpleToFluent fails. Is there a way to configure Orika to map standard JavaBean setters to fluent setters?
The culprit (at least in Orika 1.5.2) is the IntrospectorPropertyResolver, which uses java.beans:
BeanInfo beanInfo = Introspector.getBeanInfo(type);
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
this will not return properties with a fluent setter. The following subclass uses Apache beanutils, which has a FluentPropertyBeanIntrospector:
public class LenientIntrospectorPropertyResolver extends IntrospectorPropertyResolver {
private boolean includeTransientFields;
public LenientIntrospectorPropertyResolver(boolean includePublicFields) {
super(includePublicFields);
}
public LenientIntrospectorPropertyResolver(boolean includePublicFields, boolean includeTransientFields) {
super(includePublicFields, includeTransientFields);
this.includeTransientFields = includeTransientFields;
}
public LenientIntrospectorPropertyResolver() {
}
#Override
protected void collectProperties(Class<?> type, Type<?> referenceType, Map<String, Property> properties) {
PropertyUtils.addBeanIntrospector(new FluentPropertyBeanIntrospector());
final PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(type);
for (final PropertyDescriptor pd : descriptors) {
processPropertyDescriptor(type, referenceType, properties, pd);
}
}
private void processPropertyDescriptor(Class<?> type, Type<?> referenceType, Map<String, Property> properties, PropertyDescriptor pd) {
try {
Method readMethod = PropertyUtils.getReadMethod(pd);
if (readMethod == null && Boolean.class.equals(pd.getPropertyType())) {
/*
* Special handling for Boolean "is" read method; not strictly
* compliant with the JavaBeans specification, but still very common
*/
try {
readMethod = type.getMethod("is" + capitalize(pd.getName()));
} catch (NoSuchMethodException e) {
readMethod = null;
}
}
if (!includeTransientFields && (readMethod != null) && (readMethod.getAnnotation(Transient.class) != null)) {
return;
}
final Method writeMethod = PropertyUtils.getWriteMethod(pd);
processProperty(pd.getName(), pd.getPropertyType(), readMethod, writeMethod, type, referenceType, properties);
} catch (final Exception e) {
throw new RuntimeException("Unexpected error while trying to resolve property " + referenceType.getCanonicalName() + ", [" + pd.getName() + "]", e);
}
}
}
The PropertyResolver then needs to be registered
factoryBuilder.propertyResolverStrategy(new LenientIntrospectorPropertyResolver());
I have a Class A with name and value attributes.
public class A {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
I have another Class B, such as
public class B {
private String attribute01;
private String attribute01;
private String attribute01;
public String getAttribute01() {
return attribute01;
}
public void setAttribute01(String name) {
this.attribute01 = name;
}
...
}
I would like to return a list with A type, having attribute01 key and where value is getAttribute01() from B, such as ({attribute01, getAttribute01()},{attribute02, getAttribute02()}).
How to implement it?.
Thanks in advance.
Actually I can use a very stupid way, such as
public List<A> keyvalueList(final B objB) {
List<A> list = new ArrayList<>();
A objA = new A();
objA.setName("attribute01");
objA.setValue(objB.getAttribute01());
list.add(objA);
objA = new A();
objA.setName("attribute02");
objA.setValue(objB.getAttribute02());
list.add(objA);
...
return list;
}
Part of them hard coding, obvious it is not a smart way, any proposal.
I wrote sample code for List.Please check my code that is ok to use or not.I added another extra class C.in C,it has two attribute String nameFromA and String attFromB.You should add this C object to list.Following is sample code.
public class A {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public class B {
private String att1;
private String att2;
private String att3;
public String getAtt1() {
return att1;
}
public void setAtt1(String att1) {
this.att1 = att1;
}
public String getAtt2() {
return att2;
}
public void setAtt2(String att2) {
this.att2 = att2;
}
public String getAtt3() {
return att3;
}
public void setAtt3(String att3) {
this.att3 = att3;
}
}
public class C {
private String namefromA;
private String attfromB;
public String getNamefromA() {
return namefromA;
}
public void setNamefromA(String namefromA) {
this.namefromA = namefromA;
}
public String getAttfromB() {
return attfromB;
}
public void setAttfromB(String attfromB) {
this.attfromB = attfromB;
}
}
public class Test {
public static void main(String args[]){
C c = new C();
A a = new A();
B b = new B();
a.setName("A1");
b.setAtt1("100");
c.setNamefromA(a.getName());
c.setAttfromB(b.getAtt1());
List list = new ArrayList();
//use generic
list.add(c);
}
}
if you don't want to use class C,then you can use Test class like that
import java.util.ArrayList;
import java.util.List;
public class Test {
private String nameFromA;
private String valueFromB;
public Test(String nameFromA, String valueFromB) {
super();
this.nameFromA = nameFromA;
this.valueFromB = valueFromB;
}
public static void main(String args[]){
A a = new A();
B b = new B();
a.setName("A1");
b.setAtt1("100");
Test test = new Test(a.getName(),b.getAtt1());
List list = new ArrayList();
list.add(test);
}
}
This is my opinion only.Please check it is ok or not.
I need to accomplish the following (this is a simplified version):
enum Animals{
enum Cats{tabby("some value"), siamese("some value")},
enum Dogs{poodle("some value"), dachsund("some value")},
enum Birds{canary("some value"), parrot("some value")}
private String someValue = "";
private ShopByCategory(String someValue)
{
this.someValue = someValue;
}
public String getSomeValue()
{
return this.someValue;
}
}
So that I can access these items as follows:
string cat1 = Animals.Cats.tabby.getSomeValue;
string dog1 = Animals.Dogs.dachsund.getSomeValue;
string bird1 = Animals.Birds.canary.getSomeValue;
The reason why I am attempting to do this with enums is the fact that I need to be able to access each tier without having to a) instantiate a class, b) hide the names of the tiers behind a method name, or c) use an iterator to go through an EnumSet.
Is this at all possible? What would you suggest instead of enums?
//Animals.java
public class Animals {
public static class Cats {
public static final String tabby = "some value";
public static final String siamese = "some value";
}
public static class Dogs {
public static final String poodle = "some value";
public static final String dachsund = "some value";
}
public static class Birds {
public static final String canary = "some value";
public static final String parrot = "some value";
}
}
//ShopByCategory.java
public class ShopByCategory {
private String someValue;
public ShopByCategory(String value){
this.someValue = value;
}
public String getSomeValue(){
return this.someValue;
}
}
//Main.java - an example of what you can do
public class Main {
public static void main(String[] args){
ShopByCategory sbc = new ShopByCategory(Animals.Birds.canary);
System.out.println(sbc.getSomeValue());
System.out.println(Animals.Dogs.poodle);
}
}
Here's how I finally ended up implementing my solution:
public static class Animals()
{
public enum Cats()
{
tabby("some value"),
siamese("some value");
private String someValue = "";
private ShopByCategory(String someValue)
{
this.someValue = someValue;
}
public String getSomeValue()
{
return this.someValue;
}
}
public enum Dogs()
{
poodle("some value"),
dachsund("some value");
private String someValue = "";
private ShopByCategory(String someValue)
{
this.someValue = someValue;
}
public String getSomeValue()
{
return this.someValue;
}
}
public enum Birds()
{
canary("some value"),
parrot("some value");
private String someValue = "";
private ShopByCategory(String someValue)
{
this.someValue = someValue;
}
public String getSomeValue()
{
return this.someValue;
}
}
This way, I don't have to instantiate the class or call any class specific methods to get my desired info. I can get at all the "some value" strings like this:
string cat1 = Animals.Cats.tabby.getSomeValue;
string dog1 = Animals.Dogs.dachsund.getSomeValue;
string bird1 = Animals.Birds.canary.getSomeValue;