With this code:
enum MyEnum {
A(1, 2),
B(3, 4);
private final int a;
private final int b;
MyEnum(int a, int b) {
this.a = a;
this.b = b;
}
}
class GetValuesFromEnum<E extends Enum<E>> {
public void printValues() {
// This causes "cannnot find symbol" error during compilation
for (E value : E.values()) {
System.out.println(value);
}
}
}
public class Main {
public static void main(String[] args) {
new GetValuesFromEnum<MyEnum>().printValues();
}
}
Calling values() on generic enum causes an error in compile time. Is it possible to somehow retrieve values of generic enum?
in addition to #Tagir Valeev's answer, another way to retrieve enum values at runtime is through java.lang.Class.getEnumConstants() method
public void printValues(Class<? extends Enum<?>> enumCls) {
for(Object obj: enumCls.getEnumConstants()) {
System.out.println(obj);
}
}
You can modify your code in the following manner:
class GetValuesFromEnum<E extends Enum<E>> {
private Class<E> clazz;
public GetValuesFromEnum(Class<E> clazz) {
assert clazz.isEnum();
this.clazz = clazz;
}
public void printValues() {
for (E value : EnumSet.allOf(clazz)) {
System.out.println(value);
}
}
}
public class Main {
public static void main(String[] args) {
new GetValuesFromEnum<>(MyEnum.class).printValues();
}
}
To be able to access enum values in runtime, you should know the class in runtime (for example, store the Class object in the field). Generic parameter is not enough due to erasure. Also note the usage of EnumSet.allOf: usually it's very cheap way of getting all the enum constants as it reuses internal enum constants array instead of copying it.
Related
I'm having an issue regarding the return type of line 20 in method being of type Object, rather than what I would expect the type to be, which is generic E. I'm working on a larger scale program currently but have condensed the issue I'm having into the short code snippet below. Why is the arr[0].getData() in 'method' returning an Object? Any help would be very much appreciated.
public class Testing<E> {
foo[] arr;
class foo<T> {
public T data;
public foo(T in) {
data = in;
}
public T getData() {
return data;
}
}
public Testing() {
arr = new foo[5];
}
public E method() {
return arr[0].getData();
// Required type E, provided type: Object
}
}
The reason why is because you did not specify the type argument E when you created the variable foo. Instead of
foo[] arr;
the variable declaration should be
foo<E>[] arr;
Try This
public class Testing<E> {
foo[] arr;
class foo<T> {
public T data;
public foo(T in) {
data = in;
}
public T getData() {
return data;
}
}
public Testing() {
arr = new foo<E>[5] ;
}
public E method() {
return arr[0].getData();
// Required type E, provided type: Object
}
}
How can I get the values of an "enum" in a generic?
public class Sorter<T extends Enum<?>> {
public Sorter() {
T[] result = T.values(); // <- Compilation error
}
}
On the other hand, I can query the values() for Enum class:
enum TmpEnum { A, B }
public class Tmp {
void func() {
T[] result = TmpEnum.values(); // <- It works
}
}
Class::getEnumConstants
You cannot directly get it from T because generics are erased by the Java compiler so at runtime it is no longer known what T is.
What you can do is require a Class<T> object as constructor parameter. From there you can get an array of the enum objects by calling Class::getEnumConstants.
public class Sorter<T extends Enum<T>> {
public Sorter(Class<T> clazz) {
final T[] enumConstants = clazz.getEnumConstants();
}
}
another way is using interface
public interface Sorter{
default public void sorting(){
Sorter[] list=this.getClass().getEnumConstants();
}
}
use
enum TmpEnum implements Sorter { A, B }
I have an interface:
public interface ITransformer<S,T>{
public void transform(S source,T target);
default String getTransformerName(){
Class<S> s;
Class<T> t;
return s.getName() + t.getName(); //*********
}
}
the error message the starred line:
The local variable s may not have been initialized
The local variable t may not have been initialized
I would like to use this method to return a string with [S.classname][T.classname] . Please let me know how to achieve this or is this impossible to do at interface ?
Update: Jan 12
My purpose of doing this is due to the fact that this class will be in framework and I want to reduce the human error as much as possible.. I am changing the code as follows:
public interface ITransformer<S,T>{
public void transform(S source,T target);
public FieldEntry<S, T> getTransformerName();
}
public class FieldEntry<S,T> implements Comparable<FieldEntry> {
private Class<S> s;
private Class<T> t;
public FieldEntry(Class<S> s,Class<T> t){
this.s = s;
this.t = t;
}
public String getEntryName(){
return s.getName() + t.getName();
}
#Override
public int compareTo(FieldEntry entry) {
if(entry == null) throw new IllegalArgumentException("The argument to compare cannot be null!");
return entry.getEntryName().compareTo(this.getEntryName());
}
}
In order to demonstrate why this can’t work, you may change your class to
public interface ITransformer<S,T>{
public void transform(S source,T target);
static <In,Out> ITransformer<In,Out> noOp() {
return (source,target) -> {};
}
static void main(String... arg) {
ITransformer<String,Integer> t1 = noOp();
ITransformer<Long,Thread> t2 = noOp();
System.out.println(t1 == (Object)t2);
}
}
Running this will print true. In other words, both functions are represented by the same instances, so there can’t be and property allowing to recognize their different type.
Generally, when two functions (lambda expressions or method references) exhibit the same behavior, a JVM may represent them by the same implementation type or even the same instance.
Even for non-interface classes, this doesn’t work due to Type Erasure. It only works when you have a reifiable (i.e. non-generic) type extending or implementing a generic type.
It's a little bit dangerous and I wouldn't used this in production (because you should cover in your code all possible use cases of your interface), but you can use reflection for it:
public interface ITransformer<S, T> {
public void transform(S source, T target);
default String getTransformerName() {
Type[] genericInterfaces = this.getClass().getGenericInterfaces();
ParameterizedType parameterizedType = null;
for (Type genericInterface : genericInterfaces) {
if (genericInterface instanceof ParameterizedType) {
ParameterizedType paramInterface = (ParameterizedType) genericInterface;
if (paramInterface.getRawType().equals(ITransformer.class)) {
parameterizedType = paramInterface;
break;
}
}
}
if (parameterizedType == null) {
throw new IllegalStateException("!");
}
return parameterizedType.getActualTypeArguments()[0].getTypeName() + parameterizedType.getActualTypeArguments()[1].getTypeName();
}
}
public class StringToIntegerTransfomer implements ITransformer<String, Integer> {
#Override
public void transform(String source, Integer target) {
}
}
public interface StringToNumberTransfomer<T extends Number> extends ITransformer<String, T> {
}
public class StringToLongTransfomer implements StringToNumberTransfomer<Long>, ITransformer<String, Long> {
#Override
public void transform(String source, Long target) {
}
}
#Test
public void test() {
ITransformer<String, Integer> intTransformer = new StringToIntegerTransfomer();
ITransformer<String, Long> longTransformer = new StringToLongTransfomer();
ITransformer<String, String> stringTransformer = new ITransformer<String, String>() {
#Override
public void transform(String source, String target) {
}
};
ITransformer<String, Double> doubleTransformer = new StringToNumberTransfomer<Double>() {
#Override
public void transform(String source, Double target) {
}
};
System.out.println(String.format("intTransformer: %s", intTransformer.getTransformerName()));
System.out.println(String.format("longTransformer: %s", longTransformer.getTransformerName()));
System.out.println(String.format("stringTransformer: %s", stringTransformer.getTransformerName()));
System.out.println(String.format("doubleTransformer: %s", doubleTransformer.getTransformerName()));
}
Output for this snippet:
intTransformer: java.lang.Stringjava.lang.Integer
longTransformer: java.lang.Stringjava.lang.Long
stringTransformer: java.lang.Stringjava.lang.String
java.lang.IllegalStateException: !
This code has one restriction, you should say implements ITransformer<S, T> for all implementations of ITransformer. That why I have got IllegalStateException for this line ITransformer<String, Double> doubleTransformer = new StringToNumberTransfomer<Double>(). But you can improve this code.
Better option is to use some base implementation of interface and pass source and target classes into constructor:
public interface ITransformer<S, T> {
void transform(S source, T target);
String getTransformerName();
}
public abstract class BaseITransformer<S, T> implements ITransformer<S, T> {
private final Class<S> sourceClass;
private final Class<T> targetClass;
public BaseITransformer(Class<S> sourceClass, Class<T> targetClass) {
this.sourceClass = sourceClass;
this.targetClass = targetClass;
}
public String getTransformerName() {
return sourceClass.getName() + targetClass.getName();
}
}
In Java it is impossible to get a Class<S>, unless you already know which class S is, or something else that knows which class S is gives you one.
I'd like to write a type-safe code. Here's what I've tried:
public interface ResultTronsformer<T>{
public T tranform(T t);
}
public class BigDecimalTransformer implements ResultTRansformer<BigDecimal>{
public BigDecimal transform(BigDecimal t){
return t.setScale(0);
}
}
Now I define the Column interface which looks like
public interface Column{
public ResultTransformer<?> getTransformer();
}
and would like to use it in the method
public class Report{
private Map<Column, Object> columnValuePairs;
public void putIntoACollection(Column c, Object columnsValue){
ResultTransformer<?> rt = c.getTransformer();
columnValuePairs.put(c, rt.transform(o)); //Error: Couldn't convert Object
//to the capture of wildcard
}
}
How can I rearrange the design to reach the desirable type-safety? Maybe I should do the type-checking at runtime instead (throwing an exception)?
You can think about the Column just like it is some kind of container that holds specific type. In that way, you can introduce generic type in Column declaration.
public interface Column<T>{
public ResultTransformer<T> getTransformer();
}
Then, you can change Report method as follows:
public <T> void putIntoACollection(Column<T> c, T columnsValue){
ResultTransformer<T> rt = c.getTransformer();
columnValuePairs.put(c, rt.transform(columnsValue));
}
When you get the transformer, you need to specify the type because the compiler won't know it at that time.
A possible solution is to add the class as a parameter to the getTransformer of the Column and return a specialized ResultTransformer.
public interface ResultTransformer<T> {
public T transform(T t);
}
public interface Column{
public <T> ResultTransformer<T> getTransformer(Class<T> theClass);
}
public class Report{
private Map<Column, Object> columnValuePairs;
public void putIntoACollection(Column c, Object o){
ResultTransformer<Object> rt = c.getTransformer(Object.class);
columnValuePairs.put(c, rt.transform(o));
}
public interface ResultTransformer<T> {
public T transform(T t);
}
Another way would be to generalize the interface Column.
You can change the Column class and make it parametrized:
public interface Column<T> {
public ResultTransformer<T> getTransformer();
}
Then you have to parametrize the putIntoACollection method (no need to parametrize Report):
public class Report {
private Map<Column, Object> columnValuePairs;
public <T> void putIntoACollection(Column<T> c, T columnsValue) {
final ResultTransformer<T> rt = c.getTransformer();
columnValuePairs.put(c, rt.transform(columnsValue));
}
}
This way, you never need to use a capture type.
Here is an example of how you would use it:
private class BigDecimalColumn implements Column<BigDecimal> {
#Override
public ResultTransformer<BigDecimal> getTransformer() {
return new BigDecimalTransformer();
}
}
public static void main(String[] args) {
final Report report = new Report();
report.putIntoACollection(new BigDecimalColumn(), new BigDecimal("3.14"));
}
We could give up all pretenses, just use the god damned raw type
ResultTransformer rt = c.getTransformer();
A more pretentious solution -
static <T> T transform (ResultTransformer<T> rt, Object obj)
{
T t = (T)obj; // unchecked cast
return rt.transform(t);
}
public void putIntoACollection(Column c, Object obj){
ResultTransformer<?> rt = c.getTransformer();
columnValuePairs.put(c, transform(rt, obj) );
}
Say, i have a generic type as below
public class GenericType<T> {
private T someVar;
public void setVar(T var) { this.someVar = var; }
//Rest of the code
}
I want to allow it to take only specific types(String/Integer/Double). I know about bounded wildcards but they don't help me here. In setVar(), I can check the instanceof and throw an Exception if type is not Integer/String etc. Is this the best way to do it?
I have the same problem when doing operations on this type. Depending on the type, I want to do different operations. Inheritance and bounded wildcards seem like the way to go in general for this kind of problem but these are primitive wrappers.
Using Inheritance:
Parent.java
public abstract class Parent<T> {
public abstract void display(T t);
}
ChildString.java
public class ChildString extends Parent<String> {
#Override
public void display(String t) {
// Do something here...
}
}
ChildInteger.java
public class ChildInteger extends Parent<Integer> {
#Override
public void display(Integer t) {
// Do something here...
}
}
ChildDouble.java
public class ChildDouble extends Parent<Double> {
#Override
public void display(Double t) {
// Do something here...
}
}
And access the class child rather than you directly access the parent class.
Update
Here another example:
GenericType.java
public class GenericType {
public void display(Object t) {
String msg;
if(t instanceof String) {
msg = "String";
} else if (t instanceof Integer) {
msg = "Integer";
} else if (t instanceof Double) {
msg = "Double";
} else {
msg = "Another Object";
}
System.out.println(msg);
}
}
SpecificGeneric.java
public class SpecificGeneric {
public static void main(String[] args) {
GenericType basicType = new GenericType();
basicType.display(new String());
basicType.display(new Integer(1));
basicType.display(new Double(0.1));
}
}
You cannot (more than extends something, but in your case you want few unrelated types, so it does not help).
What you can, is check instance passed to method (you already know it). If you want one instace of generic class for eg. String another for Integers, but don't allow eg. Point2D, you can make constructor with parameter Class clazz and check when constructing whether its allowed.
If you are more paranoid, you can store that clazz and in all function compare whether parameter is actualy that class.
This way, you can still create MyClass, but cannot create instance with this type. (But you can cast it, co its not fool proof)
Inferring the desired type say GenericType<Double> and using instanceof when neccesary is the quickest and easy option. Alternatively overload setVar(..) to accept the restricted types in your Generic class.
public static class GenericType<T>
{
private T someVar;
public void setVar(String var)
{
this.someVar = (T) var;
}
public void setVar(Integer var)
{
this.someVar = (T) var;
}
public void setVar(Double var)
{
this.someVar = (T) var;
}
}