I have a series of enums which look like this except that the name and values are different:
/* Bone Diagnosis. Value is internal code stored in database. */
public enum BoneDiagnosis {
NORMAL(121),
ELEVATED(207),
OSTEOPENIA(314),
OSTEOPOROSIS(315);
private int value;
BoneDiagnosis(final int value) {
this.value = value;
}
/** Get localized text for the enumeration. */
public String getText() {
return MainProgram.localize(this.getClass().getSimpleName().toUpperCase() + ".VALUE." + this.name());
}
/** Convert enumeration to predetermined database value. */
public int toDB() {
return value;
}
/** Convert a value read from the database back into an enumeration. */
public static BoneDiagnosis fromDB(final Integer v) {
if (v != null) {
for (final BoneDiagnosis pc : values()) {
if (v == pc.toDB()) {
return pc;
}
}
}
return null;
}
}
I know I cannot extend enums, but is there some way to abstract this design to remove all the duplicate code in toDB(), fromDB(), and getText() that each class has? I looked at other questions like Is it possible to extend enum in Java 8? which had an example using an interface, but I could not figure out how to handle the constructor and the static method. I also cannot figure out how to remove the explicit reference to type BoneDiagnosis in the fromDB() method.
My dream would be to have each class merely be defined something like what follows, with all the other support wrapped up in whatever BoneDiagnosisComplexTypeDefinition is. Is this possible?
public enum BoneDiagnosisComplexTypeDefinition {
NORMAL(121),
ELEVATED(207);
OSTEOPENIA(314),
OSTEOPOROSIS(315)
}
You can minimize the per-enum code and the per-operation overhead using
#Target(ElementType.FIELD) #Retention(RetentionPolicy.RUNTIME)
public #interface DbId {
int value();
}
final class Helper extends ClassValue<Map<Object,Object>> {
static final Helper INSTANCE = new Helper();
#Override protected Map<Object, Object> computeValue(Class<?> type) {
Map<Object,Object> m = new HashMap<>();
for(Field f: type.getDeclaredFields()) {
if(f.isEnumConstant()) try {
Object constant = f.get(null);
Integer id = f.getAnnotation(DbId.class).value();
m.put(id, constant);
m.put(constant, id);
}
catch(IllegalAccessException ex) {
throw new IllegalStateException(ex);
}
}
return Collections.unmodifiableMap(m);
}
}
public interface Common {
String name();
Class<? extends Enum<?>> getDeclaringClass();
default int toDB() {
return (Integer)Helper.INSTANCE.get(getDeclaringClass()).get(this);
}
default String getText() {
return MainProgram.localize(
getDeclaringClass().getSimpleName().toUpperCase() + ".VALUE." + name());
}
static <T extends Enum<T>&Common> T fromDB(Class<T> type, int id) {
return type.cast(Helper.INSTANCE.get(type).get(id));
}
}
public enum BoneDiagnosis implements Common {
#DbId(121) NORMAL,
#DbId(207) ELEVATED,
#DbId(314) OSTEOPENIA,
#DbId(315) OSTEOPOROSIS;
}
Test example
int id = BoneDiagnosis.OSTEOPENIA.toDB();
System.out.println("id = " + id);
BoneDiagnosis d = Common.fromDB(BoneDiagnosis.class, id);
System.out.println("text = " + d.getText());
Note that the reflective operations are only performed once per class using ClassValue which is especially designed for caching per-class meta data efficiently, thread safe and without preventing class unloading in environments where it matters. The actual toDB and fromDB are reduced to a hash lookups.
By the way, it’s important that this code uses getDeclaringClass() rather than getClass() as enums may have specializations like in enum Foo { BAR { … } … } where getClass() returns the specialization class rather than the enum type.
The interface approach is the only way to go. Here is another way to leverage interfaces to minimize code duplication in your enums.
Update:
Some have complained about reflection and casting. Here are two options without need for casting and one that does not require reflection.
Option1 (clean no reflection but requires extra method in enums):
public static interface BoneDiagnosisType{
public String name();
public int getValue();
default int toDB() {
return getValue();
}
default String getText(){
return MainProgram.localize( this.getClass().getSimpleName().toUpperCase() + ".VALUE." + name() );
}
public static < E extends Enum<E> & BoneDiagnosisType > E fromDB(Class<E> eClass, Integer v) {
if (v != null) {
for ( final E pc : eClass.getEnumConstants() ) {
if ( v == pc.toDB() ) {
return pc;
}
}
}
return null;
}
}
public static enum BoneDiagnosis1 implements BoneDiagnosisType{
NORMAL(121),
ELEVATED(207),
OSTEOPENIA(314),
OSTEOPOROSIS(315);
int value;
BoneDiagnosis1(int value) {
this.value = value;
}
public int getValue(){
return value;
}
}
public static enum BoneDiagnosis2 implements BoneDiagnosisType{
NORMAL(1121),
ELEVATED(1207),
OSTEOPENIA(1314),
OSTEOPOROSIS(1315);
int value;
BoneDiagnosis2(int value) {
this.value = value;
}
public int getValue(){
return value;
}
}
Option2 (requires reflection keeps enums as simple as possible):
public static interface BoneDiagnosisType{
public String name();
default int toDB() {
try{
Class<?> clazz = getClass();
Field field = clazz.getDeclaredField("value");
return field.getInt(this);
}catch(RuntimeException e){
throw e;
}catch(Exception e){
throw new RuntimeException(e);
}
}
default String getText(){
return MainProgram.localize( this.getClass().getSimpleName().toUpperCase() + ".VALUE." + name() );
}
public static < E extends Enum<E> & BoneDiagnosisType > E fromDB(Class<E> eClass, Integer v) {
if (v != null) {
for ( final E pc : eClass.getEnumConstants() ) {
if ( v == pc.toDB() ) {
return pc;
}
}
}
return null;
}
}
public static enum BoneDiagnosis1 implements BoneDiagnosisType{
NORMAL(121),
ELEVATED(207),
OSTEOPENIA(314),
OSTEOPOROSIS(315);
int value;
BoneDiagnosis1(int value) {
this.value = value;
}
}
public static enum BoneDiagnosis2 implements BoneDiagnosisType{
NORMAL(1121),
ELEVATED(1207),
OSTEOPENIA(1314),
OSTEOPOROSIS(1315);
int value;
BoneDiagnosis2(int value) {
this.value = value;
}
}
And the sample printout:
System.out.println( BoneDiagnosis1.NORMAL.toDB() + " : " + BoneDiagnosis1.NORMAL.getText() + " : " +
BoneDiagnosisType.fromDB( BoneDiagnosis1.class, 121 ) );
System.out.println( BoneDiagnosis1.ELEVATED.toDB() + " : " + BoneDiagnosis1.ELEVATED.getText() + " : " +
BoneDiagnosisType.fromDB( BoneDiagnosis1.class, 207 ) );
System.out.println( BoneDiagnosis2.NORMAL.toDB() + " : " + BoneDiagnosis2.NORMAL.getText() + " : " +
BoneDiagnosisType.fromDB( BoneDiagnosis2.class, 1121 ) );
System.out.println( BoneDiagnosis2.ELEVATED.toDB() + " : " + BoneDiagnosis2.ELEVATED.getText() + " : " +
BoneDiagnosisType.fromDB( BoneDiagnosis2.class, 1207 ) );
will give:
121 : BONEDIAGNOSIS1.VALUE.NORMAL : NORMAL
207 : BONEDIAGNOSIS1.VALUE.ELEVATED : ELEVATED
1121 : BONEDIAGNOSIS2.VALUE.NORMAL : NORMAL
1207: BONEDIAGNOSIS2.VALUE.ELEVATED : ELEVATED
Here's another way you could go about it using what some call the "virtual field pattern." It reduces the amount of repeated code per enum to one getter and one field. It also avoids reflection.
First create an interface for all the methods your enums have in common. In this example we'll call it Common.
public interface Common {
int getId();
String getName();
}
Create another interface that extends Common. This will have only one abstract method, which just returns an instance of Common. Give the other methods a default implementation that delegates to the Common instance.
public interface VirtualCommon extends Common {
Common getCommon();
#Override
default int getId() {
return getCommon().getId();
}
#Override
default String getName() {
return getCommon().getName();
}
}
Now create a concrete implementation of Common.
public class CommonImpl implements Common {
private int id;
private String name;
public CommonImpl(int id, String name) {
this.id = id;
this.name = name;
}
#Override
public int getId() {
return this.id;
}
#Override
public String getName() {
return this.name;
}
}
Or, if you want to save a few lines of code, instead of CommonImpl you can put a static method on Common that returns an anonymous class.
static Common of(final int id, final String name) {
return new Common() {
#Override
public int getId() {
return id;
}
#Override
public String getName() {
return name;
}
};
}
Now you can create each of your enums and they'll only need one field and one getter. Any class or enum that implements VirtualCommon will be a Common because it has a Common.
public enum EnumImpl implements VirtualCommon {
ALPHA(1, "Alpha"),
BETA(2, "Beta"),
DELTA(3, "Delta"),
GAMMA(4, "Gamma");
private final Common common;
EnumImpl(int id, String name) {
this.common = new CommonImpl(id, name);
}
#Override
public Common getCommon() {
return this.common;
}
}
This solution still has some boilerplate bridging code.
Use an Interface to define the calling interface and implement the common code in some class.
Here is a simple example:
File: BoneDiagnosis.java
public enum BoneDiagnosis
implements
CommonStuffs
{
NORMAL(121),
ELEVATED(207),
OSTEOPENIA(314),
OSTEOPOROSIS(315);
private CommonStuffsImpl commonStuffsImpl;
private int value;
BoneDiagnosis(final int theValue)
{
value = theValue;
commonStuffsImpl = new CommonStuffsImpl();
}
#Override
public int toDB()
{
return commonStuffsImpl.toDBImplementation(value);
}
}
File: CommonStuffs.java
public interface CommonStuffs
{
int toDB();
}
File: CommonStuffsImpl.java
public class CommonStuffsImpl
{
public int toDBImplementation(
final int value)
{
return value;
}
}
Related
I have a problem with method override checks. I can detect simple override relations, but if the parent class has generics and the abstract method uses type parameters (return value/args), my code breaks down because the method description is not equal to the checked method.
Example:
public interface ISetting<T> {
public T method();
}
public class Setting implements ISetting<Integer> {
public Integer method() {
//Something
}
}
In ISetting, the method description is ()Ljava/lang/Object;
and in Setting, the method description is ()Ljava/lang/Integer;
How I can check this Override ?
On my head no thoughts come, how I can make this >~< All ideas which come to my head are bad (example: ignore check on desc, but overload method just break this idea)
Note that your issue does not only apply to generic supertype. You can also override a method with a more specific return type, with no Generics involved, e.g.
interface SomeInterface {
Object method();
}
class SomeImplementation implements SomeInterface {
#Override
public Integer method() {
return null;
}
}
You have to understand the concept of bridge methods.
A bridge method performs the task of overriding a method on the byte code level, having exactly the same parameter types and return type as the overridden method, and delegates to the actual implementation method.
Since the bridge method only consists of this invocation instruction, some type casts if required, and the return instruction, it is easy to parse such a method to find the actual method it belongs to, without dealing with the complex rules of the Generic type system.
Using, the following helper classes
record MethodSignature(String name, String desc) {}
record MethodInfo(int access, String owner, String name, String desc) {
MethodSignature signature() {
return new MethodSignature(name, desc);
}
}
final class MethodAndBridges {
MethodInfo actual;
final List<MethodInfo> bridges = new ArrayList<>();
MethodAndBridges(MethodSignature sig) {}
void set(MethodInfo mi) {
if(actual != null) throw new IllegalStateException();
actual = mi;
}
void addBridge(MethodInfo mi) {
bridges.add(mi);
}
}
We can gather the information in a form ready for checking override relations with the ASM library as follows:
class MethodCollector extends ClassVisitor {
static Map<MethodSignature, MethodAndBridges> getMethods(ClassReader cr) {
MethodCollector mc = new MethodCollector();
cr.accept(mc, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return mc.found;
}
final Map<MethodSignature, MethodAndBridges> found = new HashMap<>();
String owner, superClass;
List<String> interfaces;
protected MethodCollector() {
super(Opcodes.ASM9);
}
#Override
public void visit(int version, int acc,
String name, String sig, String superName, String[] ifNames) {
owner = name;
superClass = superName;
this.interfaces = ifNames == null? List.of(): List.of(ifNames);
}
#Override
public MethodVisitor visitMethod(
int acc, String name, String desc, String sig, String[] exceptions) {
MethodInfo mi = new MethodInfo(acc, owner, name, desc);
if((acc & Opcodes.ACC_BRIDGE) == 0) {
found.computeIfAbsent(mi.signature(), MethodAndBridges::new).set(mi);
return null;
}
return new MethodVisitor(Opcodes.ASM9) {
#Override public void visitMethodInsn(
int op, String owner, String name, String tDesc, boolean i) {
found.computeIfAbsent(new MethodSignature(name, tDesc),
MethodAndBridges::new).addBridge(mi);
}
};
}
}
To demonstrate how this work, let’s enhance your example, to address more cases
interface SupplierOfSerializable {
Serializable get();
}
interface ISetting<T extends CharSequence> extends Supplier<T>, Consumer<T> {
T get();
#Override void accept(T t);
Number method(int i);
static void method(Object o) {}
private void method(Number n) {}
}
class Setting implements ISetting<String>, SupplierOfSerializable {
public String get() {
return "";
}
#Override
public void accept(String t) {}
public Integer method(int i) {
return i;
}
static void method(Object o) {}
void method(Number n) {}
}
and check the override relations (only considering the direct interfaces, without recursion)
public class CheckOverride {
public static void main(String[] args) throws IOException {
MethodCollector mc = new MethodCollector();
new ClassReader(Setting.class.getName())
.accept(mc, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
Map<MethodSignature, MethodAndBridges> implMethods = mc.found;
Map<MethodInfo, Set<MethodInfo>> overrides = new HashMap<>();
for(String ifType: mc.interfaces) {
Map<MethodSignature, MethodAndBridges> ifMethods
= MethodCollector.getMethods(new ClassReader(ifType));
System.out.println("interface " + ifType.replace('/', '.'));
printMethods(ifMethods);
System.out.println();
ifMethods.values().removeIf(CheckOverride::nonOverridable);
implMethods.forEach((sig, method) -> {
if(nonOverridable(method)) {
overrides.putIfAbsent(method.actual, Set.of());
return;
}
var overridden = ifMethods.get(sig);
if(overridden == null && method.bridges.isEmpty()) {
overrides.putIfAbsent(method.actual, Set.of());
return;
}
Set<MethodInfo> set = overrides.compute(method.actual,
(k, s) -> s == null || s.isEmpty()? new HashSet<>(): s);
if(overridden != null) set.add(overridden.actual);
for(var mi: method.bridges) {
overridden = ifMethods.get(mi.signature());
if(overridden != null) set.add(overridden.actual);
}
});
}
System.out.println("class " + mc.owner.replace('/', '.'));
printMethods(implMethods);
System.out.println();
System.out.println("Final result");
System.out.println("class " + mc.owner.replace('/', '.'));
overrides.forEach((m,overridden) -> {
System.out.println(" " + toDeclaration(m, false));
if(!overridden.isEmpty()) {
System.out.println(" overrides");
overridden.forEach(o ->
System.out.println(" " + toDeclaration(o, true)));
}
});
}
static boolean nonOverridable(MethodAndBridges m) {
return (m.actual.access() & (Opcodes.ACC_PRIVATE|Opcodes.ACC_STATIC)) != 0
|| m.actual.name().startsWith("<");
}
static void printMethods(Map<MethodSignature, MethodAndBridges> methods) {
methods.forEach((sig, methodAndBridges) -> {
System.out.println(" "+toDeclaration(methodAndBridges.actual,false));
if(!methodAndBridges.bridges.isEmpty()) {
System.out.println(" bridges");
for(MethodInfo mi: methodAndBridges.bridges) {
System.out.println(" " + toDeclaration(mi, false));
}
};
});
}
private static String toDeclaration(MethodInfo mi, boolean withType) {
StringBuilder sb = new StringBuilder();
sb.append(Modifier.toString(mi.access() & Modifier.methodModifiers()));
if(sb.length() > 0) sb.append(' ');
String clName = mi.owner();
var mt = MethodTypeDesc.ofDescriptor(mi.desc());
if(mi.name().equals("<init>"))
sb.append(clName, clName.lastIndexOf('/') + 1, clName.length());
else {
sb.append(mt.returnType().displayName()).append(' ');
if(withType) sb.append(clName.replace('/', '.')).append('.');
sb.append(mi.name());
}
if(mt.parameterCount() == 0) sb.append("()");
else {
String sep = "(";
for(ClassDesc cd: mt.parameterList()) {
sb.append(sep).append(cd.displayName());
sep = ", ";
}
sb.append(')');
}
return sb.toString();
}
}
interface ISetting
public static void method(Object)
public abstract void accept(CharSequence)
bridges
public void accept(Object)
public abstract Number method(int)
private void method(Number)
public abstract CharSequence get()
bridges
public Object get()
interface SupplierOfSerializable
public abstract Serializable get()
class Setting
Setting()
public Integer method(int)
bridges
public Number method(int)
public void accept(String)
bridges
public void accept(Object)
public void accept(CharSequence)
static void method(Object)
public String get()
bridges
public Object get()
public CharSequence get()
public Serializable get()
void method(Number)
Final result
class Setting
public String get()
overrides
public abstract Serializable SupplierOfSerializable.get()
public abstract CharSequence ISetting.get()
Setting()
public Integer method(int)
overrides
public abstract Number ISetting.method(int)
public void accept(String)
overrides
public abstract void ISetting.accept(CharSequence)
void method(Number)
static void method(Object)
The code uses newer Java features, like var, record, and the constant API, but I think, the result is straight-forward enough for converting it to older Java versions, if really required.
We're updating a Hibernate (3.6) application that defines a custom type for money, extending org.hibernate.type.ImmutableType. It's been fairly straightforward to make it instead extend AbstractSingleColumnStandardBasicType and create a Java type descriptor to store Money as BigInteger.
However, various parts of the application use HQL queries that perform aggregate functions (usually SUM) on money fields. The old style, extending ImmutableType, automatically converted the result to Money, but with the new style, that's not happening; the result is a Long.
Does anyone know how to make Hibernate custom types automatically convert the result of aggregate functions?
Old user type:
public class MoneyUserType extends ImmutableType {
private final BigIntegerType bigIntegerType = new BigIntegerType();
#Override
public Object fromStringValue(final String string) {
final BigInteger bigInteger = (BigInteger) bigIntegerType.fromStringValue(string);
return Money.inCents(bigInteger);
}
#Override
public Object get(final ResultSet rs, final String name) throws SQLException {
final BigInteger bigInteger = (BigInteger) bigIntegerType.get(rs, name);
if (null == bigInteger) {
return null;
}
return Money.inCents(bigInteger);
}
#Override
public void set(final PreparedStatement st, final Object object, final int index) throws SQLException {
final Money money = (Money) object;
bigIntegerType.set(st, money.getAmountInCents(), index);
}
#Override
public int sqlType() {
return bigIntegerType.sqlType();
}
#Override
public String toString(final Object object) {
final Money money = (Money) object;
return bigIntegerType.toString(money.getAmountInCents());
}
public String getName() {
return Money.class.getName();
}
#SuppressWarnings("unchecked")
public Class getReturnedClass() {
return Money.class;
}
}
New user type:
public class MoneyUserType extends AbstractSingleColumnStandardBasicType<Money> {
public MoneyUserType() {
super(BigIntTypeDescriptor.INSTANCE, MoneyJavaTypeDescriptor.INSTANCE);
}
#Override
public String getName() {
return Money.class.getName();
}
}
public class MoneyJavaTypeDescriptor extends AbstractTypeDescriptor<Money> {
public static final MoneyJavaTypeDescriptor INSTANCE = new MoneyJavaTypeDescriptor();
public MoneyJavaTypeDescriptor() {
super(Money.class, ImmutableMutabilityPlan.INSTANCE);
}
#Override
public Money fromString(final String string) {
final BigInteger bigInteger = BigIntegerTypeDescriptor.INSTANCE.fromString(string);
return Money.inCents(bigInteger);
}
#Override
public <X> X unwrap(Money value, Class<X> type, WrapperOptions options) {
if (value == null) {
return null;
}
if (type.isAssignableFrom(BigInteger.class)) {
return (X) value.getAmountInCents();
}
if (type.isAssignableFrom(Long.class)) {
return (X) Long.valueOf(value.getAmountInCents().longValue());
}
if (type.isAssignableFrom(Integer.class)) {
return (X) Integer.valueOf(value.getAmountInCents().intValue());
}
throw unknownUnwrap(type);
}
#Override
public <X> Money wrap(X value, WrapperOptions options) {
if (value == null) {
return null;
}
if (Number.class.isInstance(value)) {
return Money.inCents((Number) value);
}
throw unknownWrap(value.getClass());
}
#Override
public String toString(final Money money) {
return BigIntegerTypeDescriptor.INSTANCE.toString(money.getAmountInCents());
}
}
Looks like we can work around this by adding extra setters on our DTOs and putting aliases in the queries as per Custom type / converter in conjunction with Hibernate's Transformers.aliasToBean
It's not ideal but it can be made to work.
You can add an INSTANCE static field to your MoneyUserType:
public class MoneyUserType extends AbstractSingleColumnStandardBasicType<Money> {
public static final MoneyUserType INSTANCE = new MoneyUserType();
// ...
}
then add for example the following function to your custom hibernate dialect:
public class MyPostgreSQLDialect extends PostgreSQL10Dialect
{
public MyPostgreSQLDialect()
{
registerFunction("sum_money", new StandardSQLFunction("sum", MoneyUserType.INSTANCE));
}
}
declare this dialect in your persistence.xml or hibernate.cfg.xml
and then you will be able to use the sum_money function in your hql:
Money sum = entityManager.createQuery(
"select sum_money(m.money) from MoneyEntity m",
Money.class
).getSingleResult();
I need to invoke the field accessor methods, i.e the getter of a generic enum, but cannot figure out how to invoke the methods, or more specifically how to pass a generic enum as a parameter for the invoke-method.
Thanks in advance, any help is appreciated.
this is what I'd like to do more or less.
public void(Class<? extends Enum<?>> enumType) {
Enum<?>[] enumConstants = enumType.getEnumConstants();
String[] text = new String[enumConstants.length];
String[] names = new String[enumConstants.length];
for (int i = 0; i < enumConstants.length; i++ ) {
Method[] methods = enumConstants[i].getClass().getDeclaredMethods();
for (Method m: enumConstants[i].getClass().getDeclaredMethods()) {
System.out.println(enumConstants[i].name() + ": " + m.getName() + "()");
try {
if (GET_KEY_METHOD_NAME.equalsIgnoreCase(m.getName())) {
Object value = m.invoke(I HAVE NO IDEA WHAT TO PUT HERE, "");
System.out.println(value.toString());
}
if (GET_VALUE_METHOD_NAME.equalsIgnoreCase(m.getName())) {
Object value = m.invoke(I HAVE NO IDEA WHAT TO PUT HERE, "");
System.out.println(value.toString());
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
The parameters for the Method.invoke method are always the instance the method is called for, followed by the list of parameters.
Object value = m.invoke(enumConstants[i]);
is most likely what you need.
Also you should add a type parameter to the method:
public <T extends Enum<T>> void myMethod(Class<T> enumType) {
T[] enumConstants = enumType.getEnumConstants();
BTW: Have you considered using a interface containing those methods? This would allow you to access the methods without having to use reflection.
Also take a look at the getDeclaredMethod method and keep in mind that enum constants may instances of a subclass of the enum class, so you should use the methods not containing Declared. Also find the methods for the enum class, not for the individual classes for less lookups:
For example consider the following:
public enum MyEnum implements M1M2Interface {
ONE() {
#Override
public String m1(String s) {
return "1";
}
}, TWO() {
#Override
public int m2(BigInteger i) {
return 2;
}
}
;
}
public interface M1M2Interface {
default String m1(String s) {
return "2";
}
default int m2(BigInteger i) {
return 1;
}
}
public static <T extends Enum<T>> void testEnum(Class<T> enumType) throws NoSuchMethodException {
T[] enumConstants = enumType.getEnumConstants();
Method m1 = enumType.getMethod("m1", String.class);
Method m2 = enumType.getMethod("m2", BigInteger.class);
for (int i = 0; i < enumConstants.length; i++) {
System.out.println(enumConstants[i].name() + ":");
try {
System.out.println(" m1:" + m1.invoke(enumConstants[i], "Hello World"));
System.out.println(" m2:" + m2.invoke(enumConstants[i], (BigInteger) null));
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
ex.printStackTrace();
}
}
}
getDeclaredMethod wouldn't work here, since the methods could be declared/implemented by:
The interface (declared only prior to java 8)
the enum class
the enum constant (if there is no declaration at a "higher level" the method cannot be accessed using EnumName.CONSTANT_NAME.methodName() so it's unlikely to be done...)
Reflection is rarely the correct answer to anything. Consider having your enum classes implement a common interface, like StandardCopyOption and Month do.
If you can't modify the enum classes, and if you're using Java 8, you can pass the getter method as an argument:
public <E extends Enum<E>> E findMatch(Class<E> enumClass,
Function<E, String> nameGetter,
Predicate<String> matcher) {
for (E value : EnumSet.allOf(enumClass)) {
String name = nameGetter.apply(value);
if (matcher.test(name)) {
return value;
}
}
return null;
}
Example usage:
public static enum Season {
SPRING("Spr"),
SUMMER("Sum"),
FALL("Fal"),
WINTER("Win");
private final String abbreviation;
private Season(String abbrev) {
this.abbreviation = abbrev;
}
public getAbbreviation() {
return abbreviation;
}
}
public void doStuff() {
// ...
String abbrToFind = "Sum";
Season match = findMatch(Season.class,
Season::getAbbreviation,
Predicate.isEqual(abbrToFind));
}
If you're using a version older than Java 8, you can still do the same thing, but you'll need to define and implement the interfaces yourself:
public interface Function<A, B> {
B apply(A input);
}
public interface Predicate<T> {
boolean test(T value);
}
public void doStuff() {
// ...
final String abbrToFind = "Sum";
Season match = findMatch(Season.class,
new Function<Season, String>() {
#Override
public String apply(Season season) {
return season.getAbbreviation(),
}
},
new Predicate<String>() {
#Override
public boolean test(String name) {
return Objects.equals(name, abbrToFind);
}
});
}
How can I return enums like this?
Before I was returing an int with 0 if no, 1 if yes and 2 if other. But this wasn't good way to do. So how should it be done. My code:
class SomeClass{
public enum decizion{
YES, NO, OTHER
}
public static enum yourDecizion(){
//scanner etc
if(x.equals('Y')){
return YES;
}
else if (x.equals('N')){
return NO;
}
else{
return OTHER;
}
}
}
I don't what the "//scanner etc." does, but the methods return type should be decizion:
public static decizion yourDecizion() { ... }
Furthermore, you can add the Y, N, etc. values to the enum constants:
public enum decizion{
YES("Y"), NO("N"), OTHER;
String key;
decizion(String key) { this.key = key; }
//default constructor, used only for the OTHER case,
//because OTHER doesn't need a key to be associated with.
decizion() { }
static decizion getValue(String x) {
if ("Y".equals(x)) { return YES; }
else if ("N".equals(x)) { return NO; }
else if (x == null) { return OTHER; }
else throw new IllegalArgumentException();
}
}
Then, in the method, you can just do:
public static decizion yourDecizion() {
...
String key = ...
return decizion.getValue(key);
}
I think you should do something like these, an enum class. Then you can add as many types you want and the method yourDecizion() will return the enum type depending on the given parameter.
public enum SomeClass {
YES(0),
NO(1),
OTHER(2);
private int code;
private SomeClass(int code) {
this.code = code;
}
public int getCode() {
return code;
}
public static SomeClass yourDecizion(int x) {
SomeClass ret = null;
for (SomeClass type : SomeClass.values()) {
if (type.getCode() == x)
ret = type;
}
return ret;
}
}
Change your code to:
class SomeClass{
public enum decizion {
YES, NO, OTHER
}
public static decizion yourDecizion(){
//scanner etc
if(x.equals('Y')){
return decizion.YES;
}
else if (x.equals('N')){
return decizion.NO;
}
else{
return decizion.OTHER;
}
}
}
Note: The method return type must be decizion instead of enum and decizion should have an upper case name (as all classes should).
You can get the value in below way. Here you have private constructor which will initialize the value you want to set and when the instance method value gets invoked simply return this.key.
public class Application {
enum Day {
MONDAY("Monday"), TUESDAY("Tuesday");
String key;
Day(String str) {
key = str;
}
public String value() {
return this.key;
}
}
public static void main(String[] args) {
System.out.println(Day.MONDAY.value());
}
}
This is a practical question, but I am not sure if it has a practical answer. If you have a superclass with let's say 10 subclasses, what is the most simple way to put those 10 subclasses in a collection? Right now (this may be bad design), I have put them in a static collection field in the superclass.
The motivation for this question, however, came because I had obtained the identity of one of the fields of one of the subclasses, but I needed a reference to a different field in the same subclass.
For instance, let's say the subclass has the following fields:
public class SampleSubClass extends SampleSuperClass{
...
private Object1 o_1;
private Object2 o_2;
private Object3 o_3;
...
}
Somewhere else in the program, I have only the identity of o_2, and I wanted to get at o_3.
In theory, there might be an easier way than having to put all of the instances of SampleClass in a collection somewhere. For instance, perhaps in my dreams, there is a software language out there, where the superclass DOES carry information about its subclasses, and the superclass serves as a collection in and of itself.
But nevermind that. To me now, it seems like a good way to put the collection somewhere in the program, is to use a hashmap/hashtable, and to use it as a static member of the superclass.
Please tell me there is a better way. Is there any way to reference field A in an object by having only a reference to field B in an object?
For instance, say I have an ActionPerformed method, it has a source object that is contained in the ActionEvent object parameter. How would I find the instance of the class that owned/contained that source object? What is the best way to design this?
There is no native way to find the owner of a field given the object the field references. The JVM records the number of references pointing to each object so it can do garbage collection, but it doesn't keep track of the owners of the references.
You can store the values of all the fields in a Map which maps them to their owners:
import java.util.*;
public class Super
{
static Map<Object, Super> owners = new IdentityHashMap<Object, Super>();
// IdentityHashMap will not work with primitives due to autoboxing,
// but HashMap requires all field values to have sensible implementations
// of hashCode() and equals().
/** Gets the owner associated with a field. */
public static Object getOwner(Object field)
{
return owners.get(field);
}
/** Establishes ownership over a field. */
protected void own(Object field)
{
owners.put(field, this);
}
/** Removes an ownership, but only if this is the owner. */
protected void disown(Object field)
{
if (owners.get(field) == this) owners.remove(field);
}
/** Shorthand for disown(oldField); own(newField). */
protected <T> T change(T oldField, T newField)
{
disown(oldField);
own(newField);
return newField;
}
}
public class SubA extends Super
{
protected String s;
protected Integer i;
public SubA(String aString, Integer anInt) { setS(aString); setI(anInt); }
public void setS(String aString) { s = change(s, aString); }
public void setI(Integer anInt) { i = change(i, anInt); }
public String toString() { return "SubA(" + s + "," + i + ")"; }
}
public class SubB extends Super
{
protected Object o;
public SubB(Object anObject) { setO(anObject); }
public void setO(Object anObject) { o = change(o, anObject); }
public String toString() { return "SubB(" + o + ")"; }
}
public class Demo
{
public static void main(String[] args)
{
String s1 = "String1", s2 = "String2", s3 = "String3";
Integer i1 = 111, i2 = 222;
Object o1 = new Object(), o2 = new Object();
SubA a1 = new SubA(s1, i1), a2 = new SubA(s2, i2);
SubB b = new SubB(o1);
p("s1 owner = %s", Super.getOwner(s1)); // SubA(String1,111)
p("s2 owner = %s", Super.getOwner(s2)); // SubB(String2,222)
p("s3 owner = %s", Super.getOwner(s3)); // null
p("i1 owner = %s", Super.getOwner(i1)); // SubA(String1,111)
p("i2 owner = %s", Super.getOwner(i2)); // SubA(String2,222)
p("o1 owner = %s", Super.getOwner(o1)); // SubB(java.lang.Object#...)
p("o2 owner = %s", Super.getOwner(o2)); // null
p("s1 -> s3, o1 -> o2");
a1.setS(s3);
b.setO(o2);
p("s1 owner = %s", Super.getOwner(s1)); // null
p("s3 owner = %s", Super.getOwner(s3)); // SubA(String3,111)
p("o1 owner = %s", Super.getOwner(o1)); // null
p("o2 owner = %s", Super.getOwner(o2)); // SubB(java.lang.Object#...)
}
static void p(String fmt, Object... args)
{
System.out.format(fmt, args);
System.out.println();
}
}
Or you could make the field values themselves maintain a reference to their owner, either through inheritance or using a wrapper class:
public class OwnableObject
{
protected Object owner;
public OwnableObject(Object anOwner) { owner = anOwner; }
public Object getOwner() { return owner; }
public void setOwner(Object anOwner) { owner = anOwner; }
}
public class MyString extends OwnableObject
{
protected String str = null;
public MyString(Object anOwner) { super(anOwner); }
public String toString() { return str; }
public void set(String aString) { str = aString; }
}
public class FieldWrapper<E> extends OwnableObject
{
protected E value = null;
public FieldWrapper(Object anOwner) { super(anOwner); }
public E getValue() { return value; }
public void setValue(E aValue) { value = aValue; }
}
public class Demo
{
protected MyString s = new MyString(this);
protected FieldWrapper<Integer> i = new FieldWrapper<Integer>(this);
public void setS(String aString) { s.set(aString); }
public void setI(int anInt) { i.setValue(anInt); }
public String toString() { return "Demo(" + s + "," + i.getValue() + ")"; }
public static void main(String[] args)
{
Demo d1 = new Demo();
Demo d2 = new Demo();
MyString f1 = d1.s;
FieldWrapper<Integer> f2 = d1.i;
OwnableObject f3 = d2.s;
OwnableObject f4 = d2.i;
d1.setS("one");
d2.setS("two");
d1.setI(1000);
d2.setI(2000);
p("f1 = %s, owner = %s", f1, f1.getOwner());
p("f2 = %d, owner = %s", f2.getValue(), f2.getOwner());
p("f3 = %s, owner = %s", f3, f3.getOwner());
p("f4 = %s, owner = %s", f4, f4.getOwner());
}
static void p(String fmt, Object... args)
{
System.out.format(fmt, args);
System.out.println();
}
}
Answering your direct question: how to easily define a collection containing a given set of classes?
public class ClassA {
private final List<Class<? extends a>> knownSubclasses = Arrays.asList(ClassB.class, ClassC.class);
};
class ClassB extends ClassA {}
class ClassC extends ClassA {}
Answering your motivation: how to access a field in a subclass without declaring it for the super class?
public class SomeSuperclass {
protected Object3 getObject3() throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
}
public class SomeSubclass extends SomeSuperclass {
private final Object3 object3 = null;
#Override
protected Object3 getObject3() { return object3; }
}
Maybe recognize instances having an object3 by the use of interfaces
public interface MyClassWithObject3 { Object3 getObject3(); }
...
void someOperation(SomeSuperclass that) {
if (that instanceof MyClassWithObject3) { ... }
}
You could also use named properties
void someOperation(SomeSuperClass that) {
Object3 object3 = that.getProperty("object3");
}