i would like to get rid of these warnings about unchecked conversion and parameterization without surpressing them.
interface Switch {
void toggle();
}
enum A implements Switch {
a1,a2;
#Override public void toggle() {
state=!state;
}
boolean state;
}
enum B implements Switch {
b1,b2;
#Override public void toggle() {
state=!state;
}
boolean state;
}
public class Warnings {
public static void main(String[] args) {
Class<? extends Enum>[] enums=new Class[]{A.class,B.class};
for(Class<? extends Enum> clazz:enums)
try {
Enum s=Enum.valueOf(clazz,args[0]);
((Switch)s).toggle();
} catch(IllegalArgumentException eee) {}
}
}
You can't without writing your own valueOf. Enum is defined as:
class Enum<E extends Enum<E>>
and Enum.valueOf is defined as:
public static <T extends Enum<T>> T valueOf(Class<T> enumType,
String name)
Note the recursive type parameterization which implies that you can only call valueOf with a specific enum class (e.g. A.class), but not with a generic one, as a Class<? extends Enum<?>> is not a match because the two question marks aren't assumed to represent the same (unknown) type by the compiler.
So apart from using generic collections instead of arrays, you have to write your own valueOf method that accepts any enum class.
public class Warnings {
public static void main(final String[] args) {
List<Class<? extends Enum<?>>> enums = new ArrayList<Class<? extends Enum<?>>>();
enums.add(A.class);
enums.add(B.class);
for (Class<? extends Enum<?>> clazz : enums) {
try {
Switch s = valueOf(clazz, args[0]);
s.toggle();
} catch (IllegalArgumentException eee) {
}
}
}
private static Switch valueOf(final Class<? extends Enum<?>> enumClass, final String name) {
Enum<?>[] enumConstants = enumClass.getEnumConstants();
for (Enum<?> constant : enumConstants) {
if (constant.name().equals(name)) {
return (Switch) constant;
}
}
throw new IllegalArgumentException(name + " is not a constant of enum class " + enumClass.getName());
}
}
Don't mix arrays and generics. They do not work well together because generics in java is implemented using type erasure.
This should work.
interface Switch {
void toggle();
}
enum A implements Switch {
a1, a2;
#Override
public void toggle() {
state = !state;
}
boolean state;
}
enum B implements Switch {
b1, b2;
#Override
public void toggle() {
state = !state;
}
boolean state;
}
public class Test {
public static void main(String[] args) {
List<Class<? extends Switch>> enums = new ArrayList<Class<? extends Switch>>();
enums.add(A.class);
enums.add(B.class);
for (Class<? extends Switch> clazz : enums)
try {
Switch s = clazz.getEnumConstants()[0];
((Switch) s).toggle();
} catch (IllegalArgumentException eee) {
}
}
}
Related
The idea of design is GenericFileService maintains a list of candidate instances of FileService implementations. Each instance can only handle a specific fileType. GenericFileServiceImpl implements GenericFileService and provide a generic method saveFileInfo to accept any supported fileType. I think this should work at runtime, but I got wildcard capture compile error. Could you explain why? How to fix it?
interface FileInfo {
String getFileType();
}
static class MsdsFileInfo implements FileInfo {
private String fileType;
#Override
public String getFileType() {
return fileType;
}
/*
... Other msds file specific properties
* */
}
static class NdgcFileInfo implements FileInfo {
private String fileType;
#Override
public String getFileType() {
return fileType;
}
/*
... Other ndgc file specific properties
* */
}
interface FileService<T extends FileInfo> {
void saveFileInfo(T fileInfo);
boolean support(String fileType);
}
static class MsdsFileService implements FileService<MsdsFileInfo> {
#Override
public void saveFileInfo(MsdsFileInfo fileInfo) {
// implementation for MsdsFileInfo
}
#Override
public boolean support(String fileType) {
return StringUtils.equalsIgnoreCase(fileType, "MSDS");
}
}
static class NdgcFileService implements FileService<NdgcFileInfo> {
#Override
public void saveFileInfo(NdgcFileInfo fileInfo) {
// implementation for NdgcFileInfo
}
#Override
public boolean support(String fileType) {
return StringUtils.equalsIgnoreCase(fileType, "NDGC");
}
}
interface GenericFileService {
<T extends FileInfo> void saveFileInfo(String fileType, T fileInfo);
}
static class GenericFileServiceImpl implements GenericFileService {
private final List<FileService<? extends FileInfo>> candidateFileServices = new ArrayList<>();
public GenericFileServiceImpl() {
candidateFileServices.add(new MsdsFileService());
candidateFileServices.add(new NdgcFileService());
}
#Override
public <T extends FileInfo> void saveFileInfo(String fileType, T fileInfo) {
FileService<? extends FileInfo> fileService = candidateFileServices.stream()
.filter(service -> service.support(fileType))
.findFirst()
.orElseThrow(() -> {
throw new IllegalArgumentException("Unsupported fileType.");
});
// Got wildcard capture error here
fileService.saveFileInfo(fileInfo);
}
}
Below is the error message:
error: incompatible types: T cannot be converted to CAP#1
fileService.saveFileInfo(fileInfo);
^
where T is a type-variable:
T extends FileInfo declared in method <T>saveFileInfo(String,T)
where CAP#1 is a fresh type-variable:
CAP#1 extends Object from capture of ?
I can tell you, where is the problem with this code.
Consider this piece of code you provided:
private final List<FileService<? extends FileInfo>> candidateFileServices = new ArrayList<>();
public GenericFileServiceImpl() {
candidateFileServices.add(new MsdsFileService());
candidateFileServices.add(new NdgcFileService());
}
You defined the variable candidateFileServices, type is List<FileService<? extends FileInfo>>
So, you can add new MsdsFileService() and new NdgcFileService() in this List
Then you use
FileService<? extends FileInfo> fileService = candidateFileServices.stream()...
get a fileService, type is FileService<? extends FileInfo>.
So, which Object can be hold by the fileService? MsdsFileService、NdgcFileService and any class which extends FileService
Then the code fileService.saveFileInfo(fileInfo);
Looking back at its method signature:
interface FileService<T extends FileInfo> {
void saveFileInfo(T fileInfo);
boolean support(String fileType);
}
Its argument is FileInfo or a subtype of FileInfo.
When you use that fileService to call the method saveFileInfo, In fact, at the compilation stage, the compiler cannot determine the type of its parameters at all, so it can only be represented by T, but T is FileInfo or a subtype of FileInfo.
Therefore, the java compiler will consider it unsafe
Because fileService may be MsdsFileService、NdgcFileService and any class which extends FileService, the parameter of the method saveFileInfo may be FileInfo or a any subtype of FileInfo
But you can't use MsdsFileService to call method saveFileInfo(NdgcFileInfo).
In addition, this code, it seems, is deliberately using generics, but in fact it does not need it at all:
interface FileInfo { }
class File1Info implements FileInfo { }
class File2Info implements FileInfo { }
interface FileService {
void saveFileInfo(FileInfo fileInfo);
}
class File1Service implements FileService {
#Override
public void saveFileInfo(FileInfo fileInfo) {
}
}
class File2Service implements FileService {
#Override
public void saveFileInfo(FileInfo fileInfo) { }
}
interface GenericFileService {
void saveFileInfo(FileInfo fileInfo);
}
class GenericFileServiceImpl implements GenericFileService {
private final List<FileService> candidateFileServices = new ArrayList<>();
public GenericFileServiceImpl() {
candidateFileServices.add(new File1Service());
candidateFileServices.add(new File2Service());
}
#Override
public void saveFileInfo(FileInfo fileInfo) {
candidateFileServices.forEach(fileService -> fileService.saveFileInfo(fileInfo));
}
}
If you must use generics, you need to modify it to ensure type safety:
interface GenericFileService {
<T extends FileInfo, R extends FileService<T>> void saveFileInfo(T fileInfo, R fileService);
FileService<? extends FileInfo> getFileService(String fileType);
}
class GenericFileServiceImpl implements GenericFileService {
private final List<FileService<? extends FileInfo>> candidateFileServices = new ArrayList<>();
public GenericFileServiceImpl() {
candidateFileServices.add(new MsdsFileService());
candidateFileServices.add(new NdgcFileService());
}
#Override
public FileService<? extends FileInfo> getFileService(String fileType) {
return candidateFileServices.stream()
.filter(service -> service.support(fileType))
.findFirst()
.orElseThrow(() -> {
throw new IllegalArgumentException("Unsupported fileType.");
});
}
#Override
public <T extends FileInfo, R extends FileService<T>> void saveFileInfo(T fileInfo, R fileService) {
fileService.saveFileInfo(fileInfo);
}
}
I want to implement a class that instantiates generic types.
public class DisjointSet<T extends Set<E>, E> {
private final Class<T> setType;
public DisjointSet(Class<T> setClass) {
this.setType = setClass;
}
public void doSomething(E Element) {
T set = setClass.newInstance();
set.add(element);
}
}
I tried instantiating the class like this:
DisjointSet<HashSet<Integer>, Integer> disjointSet = new DisjointSet<>(HashSet<Integer>.class);
However using .class on a generic type does not seem to be allowed. How would I correctly pass the required Class of a generic type to the constructor?
Not sure it is good to expose the inner set type (Hash versus other) in the parameterized type.
Actually due to type erasure you can't instantiate parameterised types directly, but you can pass in a factory,
package langGenerics;
import java.util.HashSet;
import java.util.Set;
public class UseGenerics {
public static void main(String[] args) {
SetFactory<Integer> setFactory = HashSet::new;
DisjointSet<Integer> disjointSet = new DisjointSet<>(setFactory);
disjointSet.doSomething( 123 );
}
}
interface SetFactory<T> { Set<T> get(); }
class DisjointSet<T> {
private SetFactory<T> setFactory;
public DisjointSet(SetFactory<T> setFactory) {
this.setFactory = setFactory;
}
public void doSomething(T item) {
Set<T> set = setFactory.get();
set.add(item);
}
}
If you really want to init your own set storage, then I suggest you to pass Supplier to your constructor:
public static class DisjointSet<T extends Set<E>, E> {
T set;
public DisjointSet(Supplier<T> supplier) {
set = supplier.get();
}
public void doSomething(E element) {
set.add(element);
}
}
Then use it:
DisjointSet<HashSet<Integer>, Integer> set = new DisjointSet<>(HashSet::new);
if this is what you wanted,
public class DisjointSet<T extends Set<E>, E> {
private final Class<T> setType;
public DisjointSet(Class<T> setClass) {
this.setType = setClass;
}
public static void main(String[] args) {
DisjointSet<HashSet<Integer>, Integer> disjointSet = new DisjointSet(new HashSet<Integer>().getClass());
}
}
I have some difficulty to simplify more the problem. Sorry if they are too many code here.
I try to improve the architecture of the code above because I hate warning and cast and I feel something wrong.
Now, the code.
I have a util class with these two parametrized methods (same signature as OpenJPA's CriteriaBuilder...)
public class MyUtil {
public void equal(List<?> l, Object value) {
// do something (see CriteriaBuilder.equal method)
}
public <Y extends Comparable<? super Y>> void greaterThan(List<? extends Y> l, Y value) {
// do something (see CriteriaBuilder.greaterThan method)
}
}
Then, I want to be able to abstract them to call it via an interface.
public interface IOperation<T> {
// maybe make this method generic ? but how ?
public abstract void doOp(List<T> l, T value);
}
public abstract class AbstractOperation<T> implements IOperation<T> {
protected MyUtil myUtil;
}
public class EqualOp extends AbstractOperation<Object> {
#Override
public void doOp(List<Object> path, Object value) {
myUtil.equal(path, value);
}
}
public class GreaterThanOp<T extends Comparable<? super T>> extends AbstractOperation<T> {
#Override
public void doOp(List<T> path, T value) {
myUtil.greaterThan(path, value);
}
}
I create a factory
public class OperationFactory {
private static OperationFactory instance;
public static OperationFactory getInstance() {...}
public IOperation<?> get(String op) {
if ("=".equals(op)) {
return new EqualOp();
} else if (">".equals(op)) {
return new GreaterThanOp<Comparable<? super Object>>();
}
throw new InvalidParameterException();
}
}
Then I use it :
public class Client {
public void needOp(String op) {
IOperation<String> operation = (IOperation<String>) OperationFactory.getInstance().get(op); // How to avoid this cast ?
List<String> l = null;
operation.doOp(l, "a string");
}
}
My question is : is it possible to avoid this cast in the Client class ? How ? Is there a way to have a better architecture ?
Thanks for reading
I'm assuming you can require your type to be Comparable.
Parameterize EqualOp like GreaterThanOp:
public class EqualOp<T extends Comparable<T>> extends AbstractOperation<T> {
#Override public void doOp(List<T> path, T value) ...
And define get() like this:
public <T extends Comparable<T>> IOperation<T> get(String op) {
if ("=".equals(op)) {
return new EqualOp<T>();
} else if (">".equals(op)) {
return new GreaterThanOp<T>();
}
...
I want to enforce enum implementation in Java but I found out that I couldn't do it.
So I decided to define an abstract function that returns the enums of subclasses. But don't know how to do it.
Here is the code:
EnumInterface
public interface EnumInterface
{
public String getString();
}
ParentClass
public abstract class ParentClass {
public abstract Enum<?> getEnums();
}
ChildClass
public class ChildClass extends ParentClass{
public enum EnumImplementation implements EnumInterface
{
FOO("foo"),
BAR("bar");
String string;
EnumImplementation(String field)
{
this.string = string;
}
#Override
public String getString() {
return string;
}
}
#Override
public Enum<?> getEnums() {
return ?;
}
}
The code above doesn't work, I'm just trying to describe my problem.
I also want to enforce the enum return type to EnumInterface if possible.
If you know how to enforce specific enum implementation that would be better as I don't even have to define the function in ParentClass.
So, how do I return the enum so that I can just do this instanceOfParentClass.getEnums().FOO?
What about this:
public <T extends Enum<T> & EnumInterface> T[] getEnums() {
return (T[])EnumImplementation.values();
}
Note the cast, which might result in a ClassCastException.
To prevent this you could pass the enum class or just return an array of EnumInterface:
public <T extends Enum<T> & EnumInterface> T[] getEnums(Class<T> enumType)
public <T extends Enum<T> & EnumInterface> EnumInterface[] getEnums()
Note that this doesn't enable you to call the method like getEnums().FOO, but you could pass the class and the name, e.g.
public <T extends Enum<T> & EnumInterface> T[] getEnum(Class<T> enumType, String enumName) {
return Enum.valueOf( enumType, enumName);
}
However, just as Tim B said, there might be a better option for what you're trying to achieve.
Try this.
EnumInterface
public interface EnumInterface {
public String getString();
}
ParentClass
public abstract class ParentClass<ENUM_TYPE extends Enum<ENUM_TYPE> & EnumInterface> {
public abstract ENUM_TYPE getEnums();
}
ChildClass
public class ChildClass extends ParentClass<ChildClass.EnumImplementation> {
public static enum EnumImplementation implements EnumInterface {
FOO("foo"),
BAR("bar");
String string;
EnumImplementation(String field) {
this.string = field;
}
#Override
public String getString() {
return string;
}
}
public EnumImplementation getEnums() {
return EnumImplementation.values()[0];
}
public static void test() {
Object result = new ChildClass().getEnums().FOO;
}
}
Note that getEnums() returns an enum value. It's not possible to return the enum container itself, but you can call other enum values from any value (f.e. EnumImplementation.FOO.BAR.FOO)
In order to map property names to database field you can use the enum valueOf method.
You need to do the mapping in your class as you cannot pass a reference to the static enumeration:
http://www.tryjava8.com/app/snippets/52b86150e4b0f5090255bc45
import java.util.*;
public class Main{
static enum TestE {
FOO,
BAR
}
static class TestC {
TestE getEnum(String name) {
return TestE.valueOf(name);
}
}
public static void main(String[] args){
System.out.println(TestE.FOO);
System.out.println(new TestC().getEnum("BAR"));
}
}
I have a question.
How I can define an Enum type for multiple static classes with a different object as Enum?
Example:
public abstract class AbstractClass
{
public Enum<?> AbstractMethod();
}
public class StaticClass extends AbstractClass
{
public enum en
{
FOO3,
FOO4
}
#Override
public Enum<en>[] AbstractMethod()
{
return en.values();
}
}
public class StaticClass2 extends AbstractClass
{
public enum en
{
FOO1,
FOO2
}
#Override
public Enum<en>[] AbstractMethod()
{
return en.values();
}
}
But that code throws an error:
The method public boolean AbstractMethod(Enum<ObjectX> en1) dosn't exsist in AbstractClass (X is the Number of the Object)
Doing this it works but throws RawTypes warning:
public abstract class AbstractClass
{
public Enum[] AbstractMethod();
}
public class StaticClass extends AbstractClass
{
public enum en
{
FOO3,
FOO4
}
#Override
public Enum[] AbstractMethod()
{
return en.values();
}
}
public class StaticClass2 extends AbstractClass
{
public enum en
{
FOO1,
FOO2
}
#Override
public Enum[] AbstractMethod()
{
return en.values();
}
}
Is this what you mean?
Enum:
public enum Fruits {
APPLE, ORANGE;
}
Classes:
public static abstract class Parent {
public abstract <E extends Enum<?>> void printEnum(final E e);
}
public static class Child extends Parent {
#Override
public <E extends Enum<?>> void printEnum(final E e) {
System.out.println(e.name());
}
}
Usage:
new Child().printEnum(Fruits.ORANGE); // Prints ORANGE