i have class something like this
class MClass
{
private int mem1,mem2,mem3.......;
public int getmem1()
{
return mem1;
}
public int getmem2()
{
return mem2;
}
......
Now I want something like this :
public int getAttr(String attr)
{
if (attr=="mem1")
return mem1;
elseif (attr=="mem2")
return mem2;
.....
How do I implement getAttr for 1000s of attr ?
Please don't ask me to make mem as array.. that is not possible due to other parts of program.
Use reflection. Reflection
This will allow you to call any public method at runtime using the name of the method as a String.
Class c = Class.forName("MyClass");
Method m = c.getMethod("get"+arg);
return (Integer) m.invoke(this);
I suggest you create a Map<String, Integer> attrMap and do
public int getAttr(String attr) {
return attrMap.get(attr);
}
You create a Map<String,Object>. As key you use the attr, as value the values.
class MyCall {
private final Map<String,Object> map = new HashMap<String,Object>();
public Object getAttr(String attr) {
return map.get(attr);
}
}
If the values will be always integers, then you can replace the generic parameter Object with Integer.
public int getAttr(String attr) {
if(map.contains(attr)) {
return map.get(attr).intValue();
} else {
reutrn ERROR_CODE; //As error or throw exception
}
}
Related
I have an enum like below. Until recently, all variables were single-valued. However, now TYPE4 can have one of three acceptable values. I was hoping to simply modify this enum to accommodate for TYPE4, but thinking perhaps having only one type that is multi-valued means I need to use an object for mapping rather than an enum. I would be grateful for any insights. Thank you.
public enum Record {
TYPE1("TYPE1"),
TYPE2("TYPE2"),
TYPE3("TYPE3"),
TYPE4_MULTI(TYPE_A or TYPE_B or TYPE_C);
private final String value;
public static final Map<Record, String> enumMap = new EnumMap<Record, String>(
Record.class);
static {
for (Record e : Record.values())
enumMap.put(e, e.getValue());
}
Record(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
Operationally, I use this enum in a factory class to determine which of 4 types of subclasses I should instantiate. I do this by have each of the subclasses know its own type like this:
#Override
public String getType() {
return Record.TYPE1.getValue();
}
,and then the factory class pre-builds a set of the subclasses like this:
#Component
public class RecordProcessorFactory {
#Autowired
public RecordProcessorFactory(List<RecordProcessor> processors) {
for (RecordProcessor recordProcessor : processors) {
processorCache.put(recordProcessor.getType(), recordProcessor);
}
}
private static final Map<String, RecordProcessor> processorCache = new HashMap<String, RecordProcessor>();
public RecordProcessor getSyncProcessor(String type) {
RecordProcessor service = processorCache.get(type);
if(service == null) throw new RuntimeException("Unknown service type: " + type);
return service;
}
}
You could use a String array to store multiple values, note that your logic may change with enumMap that way.
public enum Record {
TYPE1("TYPE1"),
TYPE2("TYPE2"),
TYPE3("TYPE3"),
TYPE4_MULTI("TYPE_A", "TYPE_B", "TYPE_C");
private final String[] values;
public static final Map<Record, String[]> enumMap = new EnumMap<Record, String[]>(Record.class);
static {
for (Record e : Record.values())
enumMap.put(e, e.getValues());
}
Record(String... values) {
this.values = values;
}
public String[] getValues() {
return values;
}
}
In case you need to get the Enum from a String value, you could add this static method:
public static Optional<Record> optionalValueOf(final String value) {
for (Record record : values()) {
for (String recordValue : record.values) {
if (null == value && null == recordValue || value.equals(recordValue)) {
return Optional.of(record);
}
}
}
return Optional.empty();
}
I think it's better to encapsulate values in the enum. It should be immutable (array is not immutable data storage).
#lombok.Getter
public enum Record {
TYPE1("TYPE1"),
TYPE2("TYPE2"),
TYPE3("TYPE3"),
TYPE4_MULTI("TYPE_A", "TYPE_B", "TYPE_C");
// immutable list
private final List<String> values;
Record(String... values) {
this.values = Arrays.stream(values)
.collect(Collectors.toList());
}
}
P.S. Map<Record, String> enumMap I think is useless, because you have a Record already and all you need just call record.getValues() instead of Record.enumMaps.get(record). Also, this is breakes OOP encapsulation.
I'm new to Java and trying to parse an XML file with objects having ENUM parameters as well. One of the ENUMs have a value as well. And at parsing I get error message (java.lang.IllegalArgumentException: No enum constant com.codecool.enums.AreaToUse.4).
the ENUM is
public enum AreaToUse {
TRAFICH(6),
TRAFICM(5),
HOMEH(4),
HOMEL(3);
private final int qualified;
AreaToUse(int qualified) {
this.qualified = qualified;
}
public int getQualified() {
return qualified;
}
}
the xml
<Lumber name="pineLong" producer="Nati" load="M" value="17.3"
qualified="4" length="3200" width="350" thickness="22"
species="pine"/>
and the parsing
for (int i = 0; i < lumbers.getLength(); i++) {
current = (Element) lumbers.item(i);
result.add(new Lumber(current.getAttribute("name"),
current.getAttribute("producer"),
Load.valueOf(current.getAttribute("load")),
Double.parseDouble(current.getAttribute("value")),
AreaToUse.valueOf(current.getAttribute("qualified")),
Integer.parseInt(current.getAttribute("length")),
Integer.parseInt(current.getAttribute("width")),
Integer.parseInt(current.getAttribute("thickness")),
current.getAttribute("species")));
}
my expected outcome is
pineLong, Nati, M, 17.3, 4, 3200, 350, 22, pine
actual return is
java.lang.IllegalArgumentException: No enum constant com.codecool.enums.AreaToUse.4
You have to write a method which returns the right Enum by the qualified value because valueOf searches an Enum by the given String. That is why you get the message No enum constant com.codecool.enums.AreaToUse.4. You have no AreaToUse called 4.
Try this:
public enum AreaToUse {
...
public static AreaToUse byQualified(int qualified) {
AreaToUse returnValue = null;
for(AreaToUse areaToUse : values()) {
if(areaToUse.getQualified() == qualified) {
returnValue = areaToUse;
}
}
return returnValue;
}
}
Instead I would recommend to create a map which maps qualified value to the area enum:
public enum AreaToUse {
private static final Map<Integer, AreaToUse> MAPPING = new HashMap<>();
static {
for(AreaToUse areaToUse : values()) {
MAPPING.put(areaToUse.getQualified(), areaToUse);
}
}
public static AreaToUse byQualified(int qualified) {
return MAPPING.get(qualified);
}
}
You should have a utility method like valueOfInt in ENUM AreaToUse which will convert your qualified int to a corresponding ENUM value.
public static AreaToUse valueOfInt(int i) {
return Arrays.stream(AreaToUse.values())
.filter(e -> e.getQualified() == i)
.findFirst()
.orElse(null);
}
And use it like:
AreaToUse.valueOfInt(current.getAttribute("qualified"))
If your current.getAttribute("qualified") returns a String then parse it to integer.
AreaToUse.valueOfInt(Integer.parseInt(current.getAttribute("qualified")))
I have implemented an enum class.This is the code:
public enum OfferType {
NO_OFFER("Pas d'offre", "N/A"),
LOCAL("Offre locale", "LOCAL"),
NATIONAL("Offre nationale", "NATIONAL"),
DEFAULT("DEFAULT", "DEFAULT");
// private static Logger logger = LoggerFactory.getLogger(OfferType.class);
public final String frontLabel;
public final String daoField;
OfferType(String frontLabel, String daoField) {
this.frontLabel = frontLabel;
this.daoField = daoField;
}
public static OfferType getEnum(String daoField) {
if (NO_OFFER.daoField.equals(daoField)){
return NO_OFFER;
}
if (LOCAL.daoField.equals(daoField)){
return LOCAL;
}
if (NATIONAL.daoField.equals(daoField)){
return NATIONAL;
}
//logger.error("Unknown enum value: " + daoField);
throw new IllegalArgumentException("No Enum specified for this string:"+daoField);
}
}
I have a class Order defined with an attribute with the type "OfferType".
I should instantiate an object "order" and set this property to the object, like the following:
order.setOfferType(OfferType.getEnum((rs.getString("offerType"))));
The problem is that with this setting I will have the value with uppercase of the enum.I want to retrieve the first value of the enum instead.How can I do it ?
Thanks in advance
Spring uses default jackson serializer. Thus in order to have custom serialization you should annotate your field:
#JsonSerialize(using = OfferTypeSerializer.class)
private OfferType offerType;
and create OfferTypeSerializer.class:
public class OfferTypeSerializer extends JsonSerializer<OfferType> {
#Override
public void serialize(OrderType value, JsonGenerator jgen,
SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString(value.frontLabel);
}
}
Another way is to get value calling offerType.frontLabel and setting it as String or implementing toString() method which is not the best solution:
#Override
public String toString() {
return this.frontLabel;
}
If you're calling getEnum(String daoField) to get data you should write it in the next form:
public static String getEnumFrontLabel(String daoField) {
if (NO_OFFER.daoField.equals(daoField)){
return NO_OFFER.frontLabel;
}
if (LOCAL.daoField.equals(daoField)){
return LOCAL.frontLabel;
}
if (NATIONAL.daoField.equals(daoField)){
return NATIONAL.frontLabel;
}
And actually better to use next form of returning enum:
public static String getEnumFrontLabel(String daoField) {
OfferType t = getType(field);
if (t != null) {
return t.frontLabel;
}
return null;
}
public static OfferType getType(String field) {
for (OfferType type : values()) {
if (type.equals(field)) {
return type;
}
}
return null;
}
I've got widely used method like:
public Map<String, Double> parseData(String[] data) {
.................
Where data is something like new String[] { "column1 -> 2.00", "column2 -> New York", ... }
Problem: It appears that data can contains both: String -> Double & String -> String values. So I need smth like:
public Map<String, String or Double> parseData(String[] data) {
................
Question: Any ideas besides return Map<String, Object>?
Create a Wrapper StringOrDouble which will look a bit like this:
public class StringOrDouble {
private String internalString;
private Double internalDouble;
public StringOrDouble(String input) {
internalString = input;
}
public StringOrDouble(Double input) {
internalDouble = input;
}
public boolean hasString() {
return internalString != null;
}
public boolean hasDouble() {
return internalDouble != null;
}
public String getString() {
return internalString;
}
public Double getDouble() {
return internalDouble;
}
}
Then have a map of type Map<String, StringOrDouble> and use that. When you use the values, you can check which one it is by testing with hasString() and/or hasDouble(). Alternatively you could have an enum which determines which type it is.
public Map<String, Container> parseData(String[] data)
You can introduce a wrapper class for this
public class Container {
private String s;
private Double d;
public Container(String s) {
this.s=s;
}
public Container(Double d) {
this.d=d;
}
public hasString() {
return s!=null;
}
public hasDouble() {
return d!=null;
}
//getters/setters
}
As far as I understand, you want something like Map<String, ? extends String || Double as the return type, but no such thing is supported in Java:
4.9 Intersection Types An intersection type takes the form T1 & ... & Tn, n>0, where Ti, 1in, are type expressions. Intersection types arise
in the processes of capture conversion (§5.1.10) and type inference
(§15.12.2.7). It is not possible to write an intersection type
directly as part of a program; no syntax supports this. The values of
an intersection type are those objects that are values of all of the
types Ti, for 1in.
So you'd better parse the input array and hold different arrays for each different type or you can use a wrapper class to represent the values in the map returned, as some other answerers explained.
Use superclass:
public Map<String, Object> parseData(String[] data)
Just an alternative to #blalasaadri. don't pretend to be better:
public static class StringDoubleValue {
private final Optional<String> sValue;
private final Optional<Double> dValue;
public MetricValue(String sValue) {
this.sValue = Optional.of(sValue);
this.dValue = Optional.absent();
}
public MetricValue(Double dValue) {
this.sValue = Optional.absent();
this.dValue = Optional.of(dValue);
}
public Object get() {
return (sValue.isPresent()) ? sValue.get() : dValue.get();
}
#Override
public String toString() {
if (sValue.isPresent()) ? sValue.get() : dValue.get().toString();
}
}
TLDR: I'd like to know how to extend fit.TypeAdaptor so that I can invoke a method that expects parameters as default implementation of TypeAdaptor invokes the binded (bound ?) method by reflection and assumes it's a no-param method...
Longer version -
I'm using fit to build a test harness for my system (a service that returns a sorted list of custom objects). In order to verify the system, I thought I'd use fit.RowFixture to assert attributes of the list items.
Since RowFixture expects the data to be either a public attribute or a public method, I thought of using a wrapper over my custom object (say InstanceWrapper) - I also tried to implement the suggestion given in this previous thread about formatting data in RowFixture.
The trouble is that my custom object has around 41 attributes and I'd like to provide testers with the option of choosing which attributes they want to verify in this RowFixture. Plus, unless I dynamically add fields/methods to my InstanceWrapper class, how will RowFixture invoke either of my getters since both expect the attribute name to be passed as a param (code copied below) ?
I extended RowFixture to bind on my method but I'm not sure how to extend TypeAdaptor so that it invokes with the attr name..
Any suggestions ?
public class InstanceWrapper {
private Instance instance;
private Map<String, Object> attrs;
public int index;
public InstanceWrapper() {
super();
}
public InstanceWrapper(Instance instance) {
this.instance = instance;
init(); // initialise map
}
private void init() {
attrs = new HashMap<String, Object>();
String attrName;
for (AttrDef attrDef : instance.getModelDef().getAttrDefs()) {
attrName = attrDef.getAttrName();
attrs.put(attrName, instance.getChildScalar(attrName));
}
}
public String getAttribute(String attr) {
return attrs.get(attr).toString();
}
public String description(String attribute) {
return instance.getChildScalar(attribute).toString();
}
}
public class MyDisplayRules extends fit.RowFixture {
#Override
public Object[] query() {
List<Instance> list = PHEFixture.hierarchyList;
return convertInstances(list);
}
private Object[] convertInstances(List<Instance> instances) {
Object[] objects = new Object[instances.size()];
InstanceWrapper wrapper;
int index = 0;
for (Instance instance : instances) {
wrapper = new InstanceWrapper(instance);
wrapper.index = index;
objects[index++] = wrapper;
}
return objects;
}
#Override
public Class getTargetClass() {
return InstanceWrapper.class;
}
#Override
public Object parse(String s, Class type) throws Exception {
return super.parse(s, type);
}
#Override
protected void bind(Parse heads) {
columnBindings = new TypeAdapter[heads.size()];
for (int i = 0; heads != null; i++, heads = heads.more) {
String name = heads.text();
String suffix = "()";
try {
if (name.equals("")) {
columnBindings[i] = null;
} else if (name.endsWith(suffix)) {
columnBindings[i] = bindMethod("description", name.substring(0, name.length()
- suffix.length()));
} else {
columnBindings[i] = bindField(name);
}
} catch (Exception e) {
exception(heads, e);
}
}
}
protected TypeAdapter bindMethod(String name, String attribute) throws Exception {
Class partypes[] = new Class[1];
partypes[0] = String.class;
return PHETypeAdaptor.on(this, getTargetClass().getMethod("getAttribute", partypes), attribute);
}
}
For what it's worth, here's how I eventually worked around the problem:
I created a custom TypeAdapter (extending TypeAdapter) with the additional public attribute (String) attrName. Also:
#Override
public Object invoke() throws IllegalAccessException, InvocationTargetException {
if ("getAttribute".equals(method.getName())) {
Object params[] = { attrName };
return method.invoke(target, params);
} else {
return super.invoke();
}
}
Then I extended fit.RowFixture and made the following overrides:
public getTargetClass() - to return my class reference
protected TypeAdapter bindField(String name) throws Exception - this is a protected method in ColumnFixture which I modified so that it would use my class's getter method:
#Override
protected TypeAdapter bindField(String name) throws Exception {
String fieldName = camel(name);
// for all attributes, use method getAttribute(String)
Class methodParams[] = new Class[1];
methodParams[0] = String.class;
TypeAdapter a = TypeAdapter.on(this, getTargetClass().getMethod("getAttribute", methodParams));
PHETypeAdapter pheAdapter = new PHETypeAdapter(fieldName);
pheAdapter.target = a.target;
pheAdapter.fixture = a.fixture;
pheAdapter.field = a.field;
pheAdapter.method = a.method;
pheAdapter.type = a.type;
return pheAdapter;
}
I know this is not a neat solution, but it was the best I could come up with. Maybe I'll get some better solutions here :-)