Java 泛型与cast
我尝试编写一个类,该类使用参数名并可以返回给定对象的相应参数。目前,我的班级是这样的:Java 泛型与cast,java,generics,reflection,Java,Generics,Reflection,我尝试编写一个类,该类使用参数名并可以返回给定对象的相应参数。目前,我的班级是这样的: public class ParamValue<T> { private String paramName; @SuppressWarnings("unchecked") public T getValue(Object obj) throws Exception { Class<?> c = obj.getClass();
public class ParamValue<T> {
private String paramName;
@SuppressWarnings("unchecked")
public T getValue(Object obj) throws Exception {
Class<?> c = obj.getClass();
Field param = c.getDeclaredField(paramName);
boolean isAccessible = param.isAccessible();
param.setAccessible(true);
Object value = param.get(obj);
param.setAccessible(isAccessible);
return (T) value;
}
// get set ...
}
ParamValue<Point> pvPoint = new ParamPoint();
ParameterizedType superclassType = (ParameterizedType) pvPoint.getClass().getGenericSuperclass();
Type paramType = superclassType.getActualTypeArguments()[0];
// print true
System.out.println(paramType.equals(Point.class));
我们可以这样做以获得长期价值:
ExampleClass ec = new ExampleClass();
ec.setValue(12L);
ParamValue<Long> pvString = new ParamValue<>();
pvString.setParamName("value");
// print 12
System.out.println(pvString.getValue(ec));
ExampleClass=newexampleclass();
ec.设定值(12L);
ParamValue pvString=新的ParamValue();
pvString.setParamName(“值”);
//打印12
System.out.println(pvString.getValue(ec));
现在,如果我将“ParamValue”声明为一个点,例如,它仍然有效:
ExampleClass ec = new ExampleClass();
ec.setValue(12L);
ParamValue<Point> pvPoint = new ParamValue<>();
pvPoint.setParamName("value");
// print 12
System.out.println(pvPoint.getValue(ec));
ExampleClass=newexampleclass();
ec.设定值(12L);
ParamValue pvPoint=新的ParamValue();
pvPoint.setParamName(“值”);
//打印12
System.out.println(pvPoint.getValue(ec));
但,由于该点不能转换为长,我预期会出现一些异常,比如ClassCastException
我知道java编译器在编译时会进行一些类型擦除,但我认为编译器会自动尝试强制转换到“pvPoint.getValue(ec)”的输出,并失败
有人能解释一下这是怎么回事吗
感谢您,ClassCastException是一个RuntimeException,这意味着它将只在运行时抛出,而不是在编译时抛出。由于您的值引用
Object value=param.get(obj)
属于Object类型,应允许强制转换为类型,因为所有类都扩展了Object。getValue方法接受任何对象作为参数,因此在编译时,无论声明的参数化类型如何,都将接受任何类
如果需要类型安全性,可以将该方法的参数声明为
您正在禁止编译器警告您正在执行未经检查的强制转换,因此编译器将无法捕获它。在运行时,类型信息不可用
如果希望代码实际抛出ClassCastException,可以添加一个存储实际目标类的字段。这样还可以删除@SuppressWarnings
注释。生成的类将如下所示:
public <T> T getValue(Class<T> returnType, Object obj) throws Exception {
Class<?> c = obj.getClass();
Field param = c.getDeclaredField(paramName);
boolean isAccessible = param.isAccessible();
param.setAccessible(true);
Object value = param.get(obj);
param.setAccessible(isAccessible);
return returnType.cast(value);
}
if (returnType.isInstance(value)) {
return returnType.cast(value);
} else {
// could be replaced with a return null
throw new ClassCastException("Exception message");
}
class ParamValue<T> {
private final Class<T> clazz;
private final String paramName;
ParamValue(Class<T> clazz, String paramName)
{
this.clazz = clazz;
this.paramName = paramName;
}
public T getValue(Object obj) throws Exception {
Class<?> c = obj.getClass();
Field param = c.getDeclaredField(paramName);
boolean isAccessible = param.isAccessible();
param.setAccessible(true);
Object value = param.get(obj);
param.setAccessible(isAccessible);
return clazz.cast(value);
}
}
ExampleClass ec = new ExampleClass();
ec.setValue(12L);
ParamValue<Point> pvPoint = new ParamValue<>(Point.class, "value");
// print 12
System.out.println(pvPoint.getValue(ec));
类参数值{
私人期末班;
私有最终字符串参数名;
ParamValue(类clazz,字符串paramName)
{
this.clazz=clazz;
this.paramName=paramName;
}
公共T getValue(对象obj)引发异常{
c类=obj.getClass();
Field param=c.getDeclaredField(paramName);
布尔值isAccessible=param.isAccessible();
参数setAccessible(true);
对象值=参数获取(obj);
参数设置可访问(可访问);
返回clazz.cast(值);
}
}
可以这样调用:
public <T> T getValue(Class<T> returnType, Object obj) throws Exception {
Class<?> c = obj.getClass();
Field param = c.getDeclaredField(paramName);
boolean isAccessible = param.isAccessible();
param.setAccessible(true);
Object value = param.get(obj);
param.setAccessible(isAccessible);
return returnType.cast(value);
}
if (returnType.isInstance(value)) {
return returnType.cast(value);
} else {
// could be replaced with a return null
throw new ClassCastException("Exception message");
}
class ParamValue<T> {
private final Class<T> clazz;
private final String paramName;
ParamValue(Class<T> clazz, String paramName)
{
this.clazz = clazz;
this.paramName = paramName;
}
public T getValue(Object obj) throws Exception {
Class<?> c = obj.getClass();
Field param = c.getDeclaredField(paramName);
boolean isAccessible = param.isAccessible();
param.setAccessible(true);
Object value = param.get(obj);
param.setAccessible(isAccessible);
return clazz.cast(value);
}
}
ExampleClass ec = new ExampleClass();
ec.setValue(12L);
ParamValue<Point> pvPoint = new ParamValue<>(Point.class, "value");
// print 12
System.out.println(pvPoint.getValue(ec));
ExampleClass=newexampleclass();
ec.设定值(12L);
ParamValue pvPoint=新的ParamValue(Point.class,“value”);
//打印12
System.out.println(pvPoint.getValue(ec));
强制转换(T)
在运行时不会执行任何操作。它只会使编译器平静下来
该方法有多个重载,其中一个重载不接受对象
参数。因此,不会在代码中执行强制转换
在这些情况下,您会看到一个ClassCastException
:
将值存储在所需类型的变量中:
final Point value = pvPoint.getValue(ec); // CCE
System.out.println(value);
为ParamValue
提供一个Class
实例以进行运行时检查:
public class ParamValue<T> {
private final Class<T> clazz;
public ParamValue(Class<T> clazz) {
this.clazz = clazz;
}
public T getValue(Object obj) throws Exception {
Class<?> c = obj.getClass();
Field param = c.getDeclaredField(paramName);
boolean isAccessible = param.isAccessible();
param.setAccessible(true);
Object value = param.get(obj);
param.setAccessible(isAccessible);
return clazz.cast(value); // instead of (T) value
}
// get set ...
}
公共类参数值{
私人期末班;
公共价值(类别分类){
this.clazz=clazz;
}
公共T getValue(对象obj)引发异常{
c类=obj.getClass();
Field param=c.getDeclaredField(paramName);
布尔值isAccessible=param.isAccessible();
参数setAccessible(true);
对象值=参数获取(obj);
参数设置可访问(可访问);
返回clazz.cast(值);//而不是(T)值
}
//准备好。。。
}
然后:
ParamValue<Point> pvPoint = new ParamValue<>(Point.class);
pvPoint.setParamName("value");
System.out.println(pvPoint.getValue(ec)); // CCE
ParamValue pvPoint=新的ParamValue(Point.class);
pvPoint.setParamName(“值”);
System.out.println(pvPoint.getValue(ec));//CCE
我发现了一件奇特的东西,以防有人进来。
如果我像这样扩展“ParamValue”类:
public class ParamPoint extends ParamValue<Point> {
}
公共类ParamPoint扩展ParamValue{
}
类型擦除仅部分应用,不会引发异常,但仍可以按如下方式测试类型:
public class ParamValue<T> {
private String paramName;
@SuppressWarnings("unchecked")
public T getValue(Object obj) throws Exception {
Class<?> c = obj.getClass();
Field param = c.getDeclaredField(paramName);
boolean isAccessible = param.isAccessible();
param.setAccessible(true);
Object value = param.get(obj);
param.setAccessible(isAccessible);
return (T) value;
}
// get set ...
}
ParamValue<Point> pvPoint = new ParamPoint();
ParameterizedType superclassType = (ParameterizedType) pvPoint.getClass().getGenericSuperclass();
Type paramType = superclassType.getActualTypeArguments()[0];
// print true
System.out.println(paramType.equals(Point.class));
ParamValue pvPoint=新的ParamPoint();
ParameteredType superclassType=(ParameteredType)pvPoint.getClass().getGenericSuperclass();
类型paramType=superclassType.getActualTypeArguments()[0];
//打印真实
System.out.println(paramType.equals(Point.class));
希望这对其他人有所帮助。您指的是哪个Point类?java.awt.Point,但我不确定这是否重要。如果编译器知道强制转换何时有效,则无需强制转换。根据定义,强制转换在运行时进行。谢谢。不幸的是,我不能使用extends来完成我想做的事情。我想我需要一些工作来验证参数化类型,但是使用类型擦除,这将是一件痛苦的事情…为什么不能使用extends?检查上面的代码,对你不起作用吗?我可以在条目中有任何类型的对象。像绳子一样,长。。。等我不能每个都有一个超类。我无法预测这个类可以处理哪个对象。根据你所说的,这意味着你有一个非常通用的类,它应该处理任何类型的方法参数和任何返回的类型。您所能做的最好的事情就是记录抛出的异常类型,并让此类的客户机在他们认为合适的时候处理它们。我建议声明每个抛出的异常(但不是RuntimeException子类),并记录ClassCastException的可能性。因为它是一个RuntimeException,所以不应该将它添加到throw子句中。