I'm working on building some classes which will represent data to be converted to JSON.
The values of these fields could be of various types (might be an int, might be a boolean).
This is an example of what I have so far (minimum reproducible example):
import javax.json.Json;
import javax.json.JsonObjectBuilder;
abstract class AttributeValue {}
class AttributeValueInt extends AttributeValue {
private int value;
AttributeValueInt( int value ) {this.value = value;}
int getValue() { return value; }
}
class AttributeValueBool extends AttributeValue {
private boolean value;
AttributeValueBool( boolean value ) {this.value = value;}
boolean getValue() { return value; }
}
class Attribute {
private AttributeValue attrValue;
Attribute( AttributeValue attrValue ) { this.attrValue = attrValue; }
AttributeValue getAttrValue() { return attrValue; }
}
class Example {
void getJSON( Attribute attribute ) {
JsonObjectBuilder builder = Json.createObjectBuilder();
builder.add( "key", attribute.getAttrValue().getValue() );
// Cannot resolve method 'getValue()'
}
}
i.e. AttributeValueInt and AttributeValueBool extend the abstract class AttributeValue. value (towards the bottom) may be either an AttributeValueInt or an AttributeValueBool.
Since both these classes implement a getValue method, I was hoping that attribute.getAttrValue().getValue() would resolve to either an int or a boolean accordingly.
The full error is this:
Error:(39, 61) java: cannot find symbol
symbol: method getValue()
location: class com.fanduel.brazepublishing.AttributeValue
How can I get this working? I thought about adding an abstract getValue method to the abstract class, but what would its return type be?
You can use a generic for that. Here an example:
abstract class AttributeValue<AttributeType> {
AttributeType value;
AttributeType getValue() {
return value;
}
}
class AttributeValueInt extends AttributeValue<Integer> {
AttributeValueInt(int value) {
this.value = value;
}
}
class AttributeValueBool extends AttributeValue<Boolean> {
AttributeValueBool(boolean value) {
this.value = value;
}
}
class Main {
static String getJson(AttributeValue<?> attribute) {
return "key: " + attribute.getValue();
}
public static void main(String[] args) {
AttributeValue<?> attributeInt = new AttributeValueInt(42);
AttributeValue<?> attributeBool = new AttributeValueBool(true);
System.out.println(getJson(attributeInt));
System.out.println(getJson(attributeBool));
}
}
Here you can call getValue on an AttributeValue instance as the type of the attribute is specified by <AttributeType>. One drawback is that you can't use primitive types anymore.
There are many ways to solve this. But since you have mentioned the AttributeValue can be of any type, you can simply use Map<K, V>
// String - key
// Object - Any value type
Map<String, Object> jsonFields = new HashMap<>();
// populate the Map
JsonObjectBuilder builder = Json.createObjectBuilder();
for(Map.Entry<String, Object> currentEntry : jsonFields.entrySet()) {
builder.add(currentEntry.getKey(), currentEntry.getValue());
}
With this way, you would have separate keys for each mapped values. Or if you still wanna stick with AttributeValue implementations, you can acheive like below. Since, Object is the parent class for all the Java classes, this will work.
abstract class AttributeValue {
public abstract Object getValue();
}
class AttributeValueInt extends AttributeValue {
private int value;
public AttributeValueInt(int value) {this.value = value;}
#Override
public Object getValue() { return value; }
}
class AttributeValueBool extends AttributeValue {
private boolean value;
public AttributeValueBool(boolean value) {this.value = value;}
#Override
public Object getValue() { return value; }
}
public class Main {
static void getJSON( Attribute attribute ) {
JsonObjectBuilder builder = Json.createObjectBuilder();
builder.add( "key", attribute.getAttrValue().getValue() );
// rest of your code
}
public static void main(String[] args) {
// your code
}
}
Related
I see this question
but I need to define a lot of enum type and do a lot of duplicate work, can every enum extends some thing? like this:
public static enum Type {
;
public final int value;
Type(int v) {
this.value = v;
}
public static Type fromInt(int v) {
for (Type type : values()) {
if (type.value == v) {
return type;
}
}
return null;
}
}
public static enum ENUM1 extends Type{
A(1), B(5), C(10);
}
public static enum ENUM2 extends Type{
D(1), E(20), F(30);
}
As seen in this question, you cannot subclass enums in java. End of story. If you describe in detail what you want to achieve, people might suggest better solution than enums.
About parsing to enum from its' int value, what you have done will work, but it would be better to do something like this:
public enum MyEnum {
A(1),
B(2),
C(3);
private final int value;
MyEnum(int value) {
this.value = value;
}
private static Map<Integer, MyEnum> valueToEnumMap;
public static MyEnum parseFromValue(int value) {
if (valueToEnumMap == null) {
valueToEnumMap = new HashMap<>();
for (MyEnum myEnum : MyEnum.values()) {
valueToEnumMap.put(myEnum.value, myEnum);
}
}
return valueToEnumMap.get(value);
}
}
Initialise a Map with keys the values of the enum, and values the enum itself, and get enums from this map. Depending on your use case, you might want to add check for duplicate keys, currently in case of duplication later pairs will overwrite previous ones.
Alternatively, you could try this.
import java.util.Arrays;
/** Int <-> Enum. */
public class EnumToIntMap
{
/**
*
* Main method.
*
* #param args commandline arguments, should they be needed.
*
*/
public static void main(String[] args)
{
interface Type
{
int value();
static <E extends Enum<E> & Type> E fromValue(Class<E> clazz, final int value)
{
return
Arrays.stream(clazz.getEnumConstants())
.parallel()
.filter(each -> each.value() == value)
.findAny()
.orElseThrow()
;
}
}
enum Enum1 implements Type
{
A(1),
B(5),
C(10),
;
private final int value;
Enum1(int value)
{
this.value = value;
}
public int value()
{
return this.value;
}
}
enum Enum2 implements Type
{
D(1),
E(20),
F(30),
;
private final int value;
Enum2(int value)
{
this.value = value;
}
public int value()
{
return this.value;
}
}
}
}
I am using Flink 1.12.0, and I have following simple test case:
I defined two model class(AbstractDataModel is super type, while the ConcreteModel is the sub type):
public interface AbstractDataModel {
public String getValue();
}
public class ConcreteModel implements AbstractDataModel {
private String key;
private String value;
public ConcreteModel() {
}
public ConcreteModel(String key, String value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
Then, I define a simple application as follows,which is to map the ConcreteModel to string,
The MapFunction is using the super type AbstractDataModel, but there is compiling error complaining:
Required type:
MapFunction<com.ConcreteModel,java.lang.String>
Provided:
MyMapFunction
I would ask how to fix this problem if I still want to use AbstractDataModel as the generic type in the MapFunction
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
class MyMapFunction implements MapFunction<AbstractDataModel, String> {
public String map(AbstractDataModel model) throws Exception {
return model.getValue();
}
}
public class ConcreteModelTest {
public static void main(String[] args) {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// env.registerType(ConcreteModel.class);
// env.registerType(AbstractDataModel.class);
//
DataStream<String> ds = env.fromElements(new ConcreteModel("a", "1"), new ConcreteModel("b", "2")).map(new MyMapFunction());
ds.print();
env.execute();
}
}
That happens basically because Flink cannot process POJO objects due to its distributed environment. Here is what says the docs:
15:45:51,460 INFO org.apache.flink.api.java.typeutils.TypeExtractor -
Class … cannot be used as a POJO type because not all fields are valid
POJO fields, and must be processed as GenericType. Please read the
Flink documentation on “Data Types & Serialization” for details of the
effect on performance.
You can use the ResultTypeQueryable and define the return type with TypeInformation.of(AbstractDataModel.class) on the method public TypeInformation getProducedType().
This interface can be implemented by functions and input formats to
tell the framework about their produced data type. This method acts as
an alternative to the reflection analysis that is otherwise performed
and is useful in situations where the produced data type may vary
depending on parametrization.
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.api.java.typeutils.ResultTypeQueryable;
public class MyMapFunction implements MapFunction<AbstractDataModel, String>,
ResultTypeQueryable {
#Override
public String map(AbstractDataModel value) throws Exception {
return value.getValue();
}
#Override
public TypeInformation getProducedType() {
return TypeInformation.of(AbstractDataModel.class);
}
}
public class ConcreteModelTest {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
AbstractDataModel concreteModel01 = new ConcreteModel("a", "1");
AbstractDataModel concreteModel02 = new ConcreteModel("a", "2");
DataStream<String> ds = env
.fromElements(concreteModel01, concreteModel02)
.map(new MyMapFunction());
ds.print();
env.execute();
}
}
or an easy way is to just call map with TypeInformation.of(String.class). Then you don't need to implement ResultTypeQueryable at MyMapFunction.
DataStream<String> ds = env
.fromElements(concreteModel01, concreteModel02)
.map(new MyMapFunction(), TypeInformation.of(String.class));
and then just use your interface with its class implementation.
public interface AbstractDataModel {
public String getValue();
}
public class ConcreteModel implements AbstractDataModel {
private String key;
private String value;
public ConcreteModel() {
}
public ConcreteModel(String key, String value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
I am trying to map JsonNullable<List<ChildRequestTO> to Nullable<List<ChildRequestDO>> (see full code below) with mapstruct 1.4.2.Final and I am facing the following error: error: Nullable<List<ChildRequestDO>> does not have an accessible constructor. If I add a constructor for Nullable like
public Nullable(T value) {
this.value = value;
this.isPresent = true;
}
then I get the following error error: Unmapped target property: "value". Mapping from property "JsonNullable<List<ChildRequestTO>> products" to "Nullable<List<ChildRequestDO>> products".
How do I map complex wrapped types in a generic way?
The following mapping code (part of ChildRequestMapper class and applied in ObjectRequestMapper) solves the problem but I want to solve it in a more generic way:
#Named("mappingHelper")
default Nullable<List<ChildRequestDO>> customMapToDOs(JsonNullable<List<ChildRequestTO>> input) {
if (JsonNullable.undefined().equals(input)) {
return Nullable.undefined();
}
if (input.get() == null) {
return Nullable.of(null);
}
var output= input.get()
.stream()
.map(this::mapToDO)
.collect(Collectors.toList());
return Nullable.of(output);
}
Changing the NullableMapper to the code below does not work/compile because I do not know how to tell mapstruct to look for the appropriate mapper to map from T to X.
public static <T, X> Nullable<X> jsonNullableToNullable(JsonNullable<T> jsonNullable) {
if (jsonNullable.isPresent()) {
return Nullable.of(jsonNullable.get());
}
return Nullable.undefined();
}
Full code:
#Mapper(
unmappedTargetPolicy = ReportingPolicy.ERROR,
uses = {ChildRequestMapper.class, NullableMapper.class}
)
public interface ObjectRequestMapper {
#Mapping(target = "slots", source = "slots", qualifiedByName = "mapToSlotDOs")
ModifyObjectRequestDO mapToDO(ModifyObjectRequestTO input);
}
#Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface ChildRequestMapper {
ChildRequestDO mapToDO(ChildRequestTO input);
}
public class NullableMapper {
public static <T> Nullable<T> jsonNullableToNullable(JsonNullable<T> jsonNullable) {
if (jsonNullable.isPresent()) {
return Nullable.of(jsonNullable.get());
}
return Nullable.undefined();
}
}
public class ModifyObjectRequestTO {
private JsonNullable<String> name = JsonNullable.undefined();
private JsonNullable<List<ChildRequestTO>> children = JsonNullable.undefined();
}
public class ModifyObjectRequestDO {
private Nullable<String> name = Nullable.undefined();
private Nullable<List<ChildRequestDO>> children = Nullable.undefined();
}
public class Nullable<T> {
private static final Nullable<?> UNDEFINED = new Nullable<>(null, false);
private final T value;
private final boolean isPresent;
private Nullable(T value, boolean isPresent) {
this.value = value;
this.isPresent = isPresent;
}
public static <T> Nullable<T> undefined() {
#SuppressWarnings("unchecked")
Nullable<T> t = (Nullable<T>) UNDEFINED;
return t;
}
public static <T> Nullable<T> of(T value) {
return new Nullable<T>(value, true);
}
public T get() {
if (!isPresent) {
throw new NoSuchElementException("Value is undefined");
}
return value;
}
public boolean isPresent() {
return isPresent;
}
}
I am looking for some help in designing the factory of concrete implementations of a generic interface. Java version 7, can not use 8+
Given such interface and abstract class:
public interface ValidationStrategy<T> {
String getNativeQuery();
ValidationStrategy<T> withValue(T value);
}
public abstract class AbstractValidationStrategy<T> implements ValidationStrategy<T> {
protected T value;
public void setValue(T value) {
this.value = value;
}
}
I want to have multiple implementations of such interface like:
public class DocumentValidationStrategy extends AbstractValidationStrategy<String> {
#Override
public String getNativeQuery() {
// here goes customer native query
return null;
}
#Override
public ValidationStrategy<String> withValue(String value) {
setValue(value);
return this;
}
}
The ValidationStrategy would be decided upon predefined enum (interface, has to be cross-platform unified) by the, ideally, a factory. The problems are generics and I can not really go around them with nor I haven't crossed any question that would address my problem
public class ValidationStrategyFactory {
private static final Map<CustomerValueValidationEnum, Class<? extends ValidationStrategy<?>>> validationStrategiesMap = new HashMap<>();
{
validationStrategiesMap.put(CustomerValueValidationEnum.VALIDATE_DOCUMENT, DocumentValidationStrategy.class);
}
private static Class<? extends ValidationStrategy<?>> getInstance(CustomerValueValidationEnum validationEnum) {
return validationStrategiesMap.get(validationEnum);
}
public static ValidationStrategy<?> createInstance(CustomerValueValidationEnum validationEnum)
throws IllegalAccessException, InstantiationException {
return getInstance(validationEnum).newInstance();
}
}
This obviously leads to problems where I can not create the proper implemntation of the ValidationStrategy interface due to my bad usage of java generics where I try to:
public boolean isValueUnique(CustomerValueValidationEnum type, Object value) {
try {
ValidationStrategyFactory.createInstance(type).withValue(value);
} catch (IllegalAccessException | InstantiationException e) {
throw new UnsupportedOperationException();
}
return false;
}
which obviously does not work as I can not feed value the way I want (value can be everything, a String, Integer or a List). I know that I am trying to combine factory and strategy patterns and I tried my best to combine both of them, I guess it is a bad pattern but now I do not really know how else can I create easily extensible validation mechanism that would only require me to create a single class.
EDIT: as requested, simple enum class that is shared between multiple services and it should not contain any business logic.
public enum CustomerValueValidationEnum {
VALIDATE_DOCUMENT("validateDocumentNumber")
;
private final String name;
private CustomerValueValidationEnum(String name) {
this.name = name;
}
#ValueMapKey
public String getName() {
return this.name;
}
}
It is impossible to type dynamically any generic type as it's checked during compilation. I suggest you to make your factory switch on your enum (using/or not a Map).
Implementation without Map :
enum CustomerValueValidationEnum { // Not provided by OP
VALIDATE_DOCUMENT,
VALIDATE_NUMBER
}
interface ValidationStrategy<T> {
String getNativeQuery();
ValidationStrategy<T> withValue(T value);
}
abstract class AbstractValidationStrategy<T> implements ValidationStrategy<T> {
protected T value;
public void setValue(T value) {
this.value = value;
}
#Override
public String getNativeQuery() {
return null;
}
#Override
public ValidationStrategy<T> withValue(T value) {
setValue(value);
return this;
}
}
class DocumentValidationStrategy<T> extends AbstractValidationStrategy<T> {
#Override
public String getNativeQuery() {
return "Customer Query";
}
}
class ValidationStrategyFactory {
// Generic types are checked during compilation time, can't type it dynamically
public static ValidationStrategy<?> createInstance(CustomerValueValidationEnum validationEnum) {
ValidationStrategy valStrat = null;
switch(validationEnum) {
case VALIDATE_DOCUMENT:
valStrat = new DocumentValidationStrategy<String>();
case VALIDATE_NUMBER:
valStrat = new DocumentValidationStrategy<Integer>();
}
return valStrat;
}
}
Implementation with Map :
import java.util.HashMap;
import java.util.Map;
enum CustomerValueValidationEnum { // Not provided by OP
VALIDATE_DOCUMENT(String.class),
VALIDATE_NUMBER(Integer.class);
private Class validationType;
CustomerValueValidationEnum(Class cls) {
validationType = cls;
}
public Class getValidationType() {
return validationType;
}
}
interface ValidationStrategy<T> {
String getNativeQuery();
ValidationStrategy<T> withValue(T value);
}
abstract class AbstractValidationStrategy<T> implements ValidationStrategy<T> {
protected T value;
public void setValue(T value) {
this.value = value;
}
#Override
public String getNativeQuery() {
return null;
}
#Override
public ValidationStrategy<T> withValue(T value) {
setValue(value);
return this;
}
}
class DocumentValidationStrategy<T> extends AbstractValidationStrategy<T> {
#Override
public String getNativeQuery() {
return "Customer Query";
}
}
class ValidationStrategyFactory {
private static final Map<Class, ValidationStrategy> validationStrategiesMap = new HashMap<>();
{
validationStrategiesMap.put(String.class, new DocumentValidationStrategy<String>());
validationStrategiesMap.put(Integer.class, new DocumentValidationStrategy<Integer>());
}
private static ValidationStrategy<?> getInstance(CustomerValueValidationEnum validationEnum) {
return validationStrategiesMap.get(validationEnum.getValidationType());
}
}
You can't use generic type through enum (without implementing an interface) : Post
You can't type dynamically any generic type : Post
One workaround is using a way to get each generic type strategy with a separate method getting from a separate map.
The lower number of various strategy generic types, the more appropriate this way is.
public class StrategyFactory {
static final Map<CustomerValueValidationEnum, ValidationStrategy<String>> validationStringStrategiesMap = new HashMap<>() {{
validationStringStrategiesMap.put(CustomerValueValidationEnum.VALIDATE_DOCUMENT_STRING, new DocumentStringValidationStrategy());
}};
static final Map<CustomerValueValidationEnum, ValidationStrategy<Integer>> validationIntegerStrategiesMap = new HashMap<>() {{
validationIntegerStrategiesMap.put(CustomerValueValidationEnum.VALIDATE_DOCUMENT_INTEGER, new DocumentIntegerValidationStrategy());
}};
public static ValidationStrategy<String> stringStrategy(CustomerValueValidationEnum e) {
return validationStringStrategiesMap.get(e);
}
public static ValidationStrategy<Integer> integerStrategy(CustomerValueValidationEnum e) {
return validationIntegerStrategiesMap.get(e);
}
}
public class DocumentStringValidationStrategy extends AbstractValidationStrategy<String> { ... }
public class DocumentIntegerValidationStrategy extends AbstractValidationStrategy<Integer> { ... }
Advantages:
The generic type will be always inferred: StrategyFactory.integerStrategy(null).withValue(1); which means the user-call is very comfortable.
Scales with a low number of generic types: 2 generic type of strategies -> 2 maps -> 2 methods.
Disadvantage:
The user must know if the String-type or Integer-type is to be requested.
Doesn't scale with a high number of generic types: if each strategy has a custom type, then this solution will not help you at all.
Characteristics:
Not null-safe, the map can return null (I'd use null-object pattern for safe behavior). This would be issue even in any of your solutions
I want to define map in Java, which keys are enums, and types of value depend of key. For example, suppose that we have following enum type:
enum KeyType {
HEIGHT(Integer.class),
NAME(String.class),
WEIGHT(Double.class)
// constructor and getter for Class field
}
and some map:
Map< KeyType, Object > map = new EnumMap<>(KeyType.class);
Is there any simple and safe way to write generic method:
public < T > T get(KeyType key) {
//...
}
that would get value from that map and cast it to corresponding with type class?
UPDATE!!!:
With this in mind:
enum KeyType {
//your enums ...
private final Class val;
//constructor ...
//and generic(!) access to the class field:
<T> Class<T> val() {
return val;
}
}
...this is possible:
public <T> T get(KeyType key) {
return (T) key.val().cast(map.get(key));
}
Your map definition would need to be
Map< KeyType, ?> map = new EnumMap<>(KeyType.class);
If you specify Object as a generic type, only actual instances of Object are allowed, not sub-types.
I don't believe there's any straight forward, generic way (no pun intended) to do what you want. You would need to create some mapping function that translates the object to the correct type based on the enum.
You can't do it with enums. But you could write a "fake" enum (the way Java code did it before Java 1.5, with private constructors and public static instances), and attach a generic type to each constant:
import java.io.Serializable;
import java.util.Map;
public final class KeyType<T>
implements Serializable {
private static final long serialVersionUID = 1;
public static final KeyType<Integer> HEIGHT =
new KeyType<>("HEIGHT", Integer.class);
public static final KeyType<String> NAME =
new KeyType<>("NAME", String.class);
public static final KeyType<Double> WEIGHT =
new KeyType<>("WEIGHT", Double.class);
private static final KeyType<?>[] allValues = {
HEIGHT, NAME, WEIGHT
};
/** #serial */
private final String name;
/** #serial */
private final Class<T> type;
private KeyType(String name,
Class<T> type) {
this.name = name;
this.type = type;
}
public String name() {
return name;
}
public Class<T> getType() {
return type;
}
#Override
public String toString() {
return name();
}
public static KeyType<?>[] values() {
return allValues.clone();
}
public static KeyType<?> valueOf(String name) {
for (KeyType<?> value : allValues) {
if (value.name.equals(name)) {
return value;
}
}
throw new IllegalArgumentException("No such value: \"" + name + "\"");
}
#Override
public boolean equals(Object obj) {
return (obj instanceof KeyType &&
this.name.equals(((KeyType<?>) obj).name));
}
#Override
public int hashCode() {
return name.hashCode();
}
public T getValue(Map<KeyType<?>, ?> map) {
return type.cast(map.get(this));
}
}