I have a Java class that does nothing else than safely cast a number of objects to their expected classes, and returns a descriptive error message if the cast fails. They all look like this:
public static RequirementId castToRequirementID(Object value) throws WrongObjectTypeException {
Class<?> expectedClass = RequirementId.class;
Class<?> actualClass = value.getClass();
if(actualClass == expectedClass) {
RequirementId requirementID = (RequirementId) value;
return requirementID;
}
String errorMessage = "Value was of wrong type";
throw new WrongObjectTypeException(errorMessage, expectedClass, actualClass);
}
public static RequirementProjectInformation castToRequirementProjectInformation(Object value) throws WrongObjectTypeException {
Class<?> expectedClass = RequirementProjectInformation.class;
Class<?> actualClass = value.getClass();
if(actualClass == expectedClass) {
RequirementProjectInformation requirementProjectInformation = (RequirementProjectInformation) value;
return requirementProjectInformation;
}
String errorMessage = "Value was of wrong type";
throw new WrongObjectTypeException(errorMessage, expectedClass, actualClass);
}
As you can see, these functions are mostly identical, except for the fact that I need to change the expectedClass in the first line of each function, and the cast in the fourth line of each function. That makes me want to generalize this in some way, for example:
public static MongoDB_ObjectAddress safelyCast(Object value, Class<?> expectedClass) throws WrongObjectTypeException {
Class<?> actualClass = value.getClass();
if(actualClass == expectedClass) {
expectedClass castedObject = (expectedClass) value;
return castedObject;
}
String errorMessage = "Value was of wrong type";
throw new WrongObjectTypeException(errorMessage, expectedClass, actualClass);
}
However, the above statement does not work since I can't use expectedClass in place of the actual class name in the declaration and the cast.
Is there some clever way to do this? Or is this simply impossible in Java?
You can use the Class.cast method:
public static <T> T safelyCast(Object value, Class<T> expectedClass) throws WrongObjectTypeException {
if (expectedClass.isInstance(value)) {
return expectedClass.cast(value);
} else {
String errorMessage = "Value was of wrong type";
throw new WrongObjectTypeException(errorMessage, expectedClass, value.getClass());
}
}
I must say I don't see much point in this WrongObjectTypeException, it doesn't give you more information than the ClassCastException you would get normally.
Related
The method javax.tools.ToolProvider.getSystemTool(Class clazz, String moduleName, String className) has the following body (JDK 12):
private static <T> T getSystemTool(Class<T> clazz, String moduleName, String className) {
try {
ServiceLoader<T> sl = ServiceLoader.load(clazz, ClassLoader.getSystemClassLoader());
for (Iterator<T> iter = sl.iterator(); iter.hasNext(); ) {
T tool = iter.next();
if (matches(tool, moduleName))
return tool;
}
} catch (ServiceConfigurationError e) {
throw new Error(e);
}
return null;
}
I don't understand the reason why authors used the widest Error class instead of something like throw new ToolProviderError(e) or even without rethrowing at all? Any rethrow with wide error classes doesn't help to understand what is happened here without digging into the exception chain.
public void etisLogAround(ProceedingJoinPoint joinPoint, EtisLog etisLog) throws Throwable {
Object[] args = joinPoint.getArgs();
MethodSignature methodSignature = (MethodSignature) joinPoint.getStaticPart().getSignature();
Method method = methodSignature.getMethod();
String[] paramNames = ((MethodSignature) joinPoint
.getSignature()).getParameterNames();
for(String paramName: paramNames) {
logger.info("paramName:" +paramName);
}
try {
Object result = joinPoint.proceed();
if(methodSignature instanceof MethodSignature) {
final Class<?>[] parameterTypes = methodSignature.getParameterTypes();
for(final Class<?> pt : parameterTypes){
logger.info("Parameter type:" + pt);
}
}
#SuppressWarnings("unchecked")
ResponseEntity<CaseOutlineHeader> returnValue = (ResponseEntity<CaseOutlineHeader>) result;
result = etisLog.trasactionDetail().toString()+" "+returnValue.getBody().getCode().toString();
} catch (IllegalArgumentException e) {
throw e;
}
}
The class CaseOutlineHeader is what I want to be changed. the parameterTypes variable contains the name of the class that I would like to pass inside the tag of the ResponseEntity<>. What if I would like to pass a different class Name. How should I do that to be flexible to accept the different class name?
If i do : ResponseEntity<parameterTypes> returnValue = (ResponseEntity<parameterTypes>) result;
it will say an error parameterTypes cannot be resolved to a type.
The problem is that your AOP method need to cast the result to something in order to get the code value it needs to log. That something must be known in advance, since you can't use type parameters in annotations, and therefore can't pass it to AOP methods. This means that all methods you access in AOP must come from a known interface, like this:
public interface LogCodeProvider {
String getLogCode();
}
public class CaseOutlineHeader implements LogCodeProvider {
#Override
public String getLogCode() {
return "My Code";
}
}
And then in your AOP method you can do like this:
#SuppressWarnings("unchecked")
ResponseEntity<LogCodeProvider> returnValue = ResponseEntity<LogCodeProvider>) result;
result = etisLog.trasactionDetail().toString()+" "+returnValue.getBody().getLogCode();
In my example I have implemented special method getLogCode() which returns a string, so each class can decide exactly what to output.
It does however look confusing to reuse the result variable to store the value returned from etisLog.trasactionDetail().
Below sample code ,
ResponseEntity<?> anyRandomMethod(){
if(any condition){
return new ResponseEntity<Animal>(new Animal(), httpstatus.OK);
}else{
return new ResponseEntity<SpaceShip>(new SpaceShip(), httpstatus.OK);
}
}
Note: this question is more about generics than it is about enums.
I have few enum types, all implementing a common interface IEffect.
For example
enum ElementalEffect implements IEffect {
FIRE, WATER;
}
enum CombatEffect implements IEffect {
PARALYSIS, SLEEP;
}
I would like to parse a config-file, that should add effects to a weapon. For that I have to resolve a given name to one of the effects. To keep it simple to maintain, I thought of writing a method like this (mostly pseudo-code, this does not compile. In fact the point of the question is how to make this compile):
IEffect resolveEffectName(String name, Class... clazzes) {
for(Class clazz : clazzes) {
try {
return Enum.valueOf(clazz, name);
} catch(IllegalArgumentException) { /* ignore, try next class */}
}
throw new IllegalArgumentException("No matching effect found for " + name);
}
// resolveEffectName(readNameFromFile, ElementalEffect.class, CombatEffect.class);
Now the problem I have is that I can't figure out how to write that method without the compiler telling me
The method valueOf(Class<T>, String) in the type Enum is not applicable for the arguments ...
People saying it should be
private static ICombatEffectType getFirstResolved(String name, Class<? extends Enum<?>>... classes) {
for (Class<? extends Enum<?>> clazz : classes) {
try {
return Enum.valueOf(clazz, name);
} catch (IllegalArgumentException e) {
}
}
return null;
}
This is not working. Feel free to try it (if you don't believe me).
The method valueOf(Class<T>, String) in the type Enum is not applicable for the arguments (Class<capture#6-of ? extends Enum<?>>, String)
You can write this fluently, if that's your style:
class FluentGetter {
private final String name;
private IEffect found;
FluentGetter(String name) { this.name = name; }
<T extends Enum<T> & IEffect> FluentGetter search(Class<T> clazz) {
if (found == null) { // If you've already found something, don't overwrite that.
try {
found = Enum.valueOf(clazz, name);
} catch (IllegalArgumentException e) {}
}
return this;
}
IEffect get() {
return found; // + check if it's null, if you want.
}
}
Then:
IEffect effect =
new FluentGetter(name)
.search(ElementalEffect.class)
.search(CombatEffect.class)
.get();
This avoids the problem of the generic bounds on the array of classes by having separate method calls for each.
Pretty sure I wouldn't use this myself; just tossing it out as an option.
What you really want is a map of names to enum constants, which you can easily make using streams:
private static final Map<String, IEffect> constants
= Stream.of(ElementalEffect.values(), CombatEffect.values())
.flatMap(Arrays::stream)
.collect(Collectors.toMap(Enum::name, Function.identity()));
IEffect resolveEffectName(String name) {
if(!constants.containsKey(name))
throw new IllegalArgumentException("No matching effect found for " + name);
return constants.get(name);
}
I found a way to make everything work without warnings using a wrapper class:
private static class Wrapper<T extends Enum<T> & IEffect> {
private Class<T> clazz;
public Wrapper(Class<T> clazz) {
this.clazz = clazz;
}
public IEffect resolveName(String name) {
return Enum.valueOf(clazz, name);
}
}
private static IEffect getFirstResolved(String name, Wrapper<?>... clazzes) {
for (Wrapper<?> clazz : clazzes) {
try {
return clazz.resolveName(name);
} catch (IllegalArgumentException e) {}
}
throw new IllegalArgumentException("No enum has a member " + name);
}
// Example call
ICombatEffectType elemental = getFirstResolved(
type,
new Wrapper<>(ElementalType.class),
new Wrapper<>(StatusEffect.class));
This should compile (with warnings), using your first method:
return (IEffect) Enum.valueOf((Class<Enum>) clazz, name);
I've got a simple Method that returns me always "-undefined-".
public static String getStereoType(Class<?> clazz) {
String result = "-undefined-";
if (clazz.isEnum()) {
result = "enum";
} else if (clazz.isInterface()) {
result = "interface";
} else if (clazz.isLocalClass() || clazz.isMemberClass()) {
result = "class";
}
return result;
}
When I call this Method with Object.class or Long.class always is the result "-undefined-".
List<Class<?>> superClazzes = ClassUtil.getSuperClazzList(clazz);
for (Class<?> c: superClazzess){
String stereoType = ClassUtil.getStereoType(c.getClass());
}
public static List<Class<?>> getSuperClazzList(Class<?> clazz) {
List<Class<?>> resultList = new ArrayList<Class<?>>();
Class<?> superClass = clazz.getSuperclass();
if (superClass != null) {
resultList.add(superClass);
resultList.addAll(getSuperClazzList(superClass));
}
return resultList;
}
What are you trying to get? So what are the possible stereo types you need? What I know of is:
Enum
Interface
Primitive (e.g. double, but not Double)
Class (everything else!)
And from the class API I can find that you might also differ between:
annotation (actual an interface in java)
synthetic (I'm not sure what this is)
local (defined inside a method)
But they might not be interesting for ULM diagrams at all.
Implementation of toString() from the JVM Class is:
public String toString() {
return (isInterface() ? "interface " : (isPrimitive() ? "" : "class ")) + getName();
}
This might give you some hint as well.
Hope it helps.
Edit: This should do the job:
public static String getStereoType(Class<?> clazz) {
String result = "class";
if (clazz.isEnum()) {
result = "enum";
} else if (clazz.isInterface()) {
result = "interface";
}
else if (clazz.isPrimitive()) {
result = "primitive";
}
return result;
}
To answer the question in your title, isLocalClass() does not always return false: it returns true for types declared within a method. Similarly, isMemberClass() returns true for types declared within another type.
Consider:
public class Outer {
interface MemberClass {}
public static void main(String[] args) {
class LocalClass {}
System.out.printf(
"%s/%s%n",
LocalClass.class.isLocalClass(),
LocalClass.class.isMemberClass()
);
System.out.printf(
"%s/%s%n",
MemberClass.class.isLocalClass(),
MemberClass.class.isMemberClass()
);
}
}
This code, when executed, prints out true/false followed by false/true. Together, they only account for types which are defined within another class, or within a method. Neither strictly depends on the target type being a class as opposed to an interface or enum, so you cannot use it to filter that way.
See #Tarion's excellent answer for the approach you should be taking (and accept his answer, as it more completely solves your problem).
How would one go about overloading the getCause() method in a throwable object ?
I have the following but it doesn't seem to work as it says that it cannot be overloaded with a string.
public class MyException extends RuntimeException {
String cause;
MyException(String s) {
cause = s;
}
#Overwrite public String getCause() {
return cause;
}
It is illegal to have two methods that only differ in their return type. Suppose someone wrote:
Object obj = myException.getCause();
That is perfectly legal java, and the compiler has no way to figure out if it's the String version or the Throwable version.
Likewise you can't replace the superclass signature since this is also perfectly legal:
Throwable t = new MyException();
Throwable t0 = t.getCause();
//Returns String?!?!?!?
Accepted answer clears the point :
It is illegal to have two methods that only differ in their return
type
But if you have a situation where, getCause() should return the custom cause in MyException, in case original cause is null.
In that case, you can use initCause() to set the cause and override toString() method. So, when getCause() method will be called on object of MyException, it will show the message from customCause instead of null.
What is the use: In legacy system, if you have used getCause() on MyException object while logging, and now you want to add the custom cause to it without changing lot of code, here is the way.
public class MyException extends RuntimeException {
String customCause;
MyException(String s) {
super(s);
customCause = s;
}
#Override
public synchronized Throwable getCause() {
if (super.getCause() != null) {
return this;
} else {
this.initCause(new Throwable(customCause));
return this;
}
}
#Override
public String toString() {
String s = getClass().getName();
String message = getLocalizedMessage();
if (message == null) {
message = customCause;
}
return (message != null) ? (s + ": " + message) : s;
}
}
References:
https://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html#initCause(java.lang.Throwable)
https://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html