Если ваши изображения названы, вы можете ссылаться на них через DOM и изменять источник.
document["imgName"].src="../newImgSrc.jpg";
или
document.getElementById("imgName").src="../newImgSrc.jpg";
Короткий ответ заключается в том, что в Java нет способа узнать тип стандартных параметров типа Java. Я рекомендую прочитать главу о стирании стилей в учебнике Java Tutorial для более подробной информации.
Популярным решением для этого является передача Class
параметра типа в конструктор общий тип, например
class Foo<T> {
final Class<T> typeParameterClass;
public Foo(Class<T> typeParameterClass) {
this.typeParameterClass = typeParameterClass;
}
public void bar() {
// you can access the typeParameterClass here and do whatever you like
}
}
Я искал способ сделать это сам, не добавляя дополнительной зависимости от пути к классам. После некоторого исследования я обнаружил, что он доступен , если у вас есть общий супертип. Это было хорошо для меня, поскольку я работал с слоем DAO с супертипом общего уровня. Если это соответствует вашему сценарию, то это самый аккуратный подход IMHO.
Большинство дженериков используют случаи, с которыми я столкнулся, имеют какой-то общий супертип, например. List<T>
для ArrayList<T>
или GenericDAO<T>
для DAO<T>
и т. д.
Статья Доступ к родовым типам во время выполнения в Java объясняет, как вы можете это сделать, используя чистую Java.
В моем проекте использовалась Spring , которая еще лучше поскольку Spring имеет удобный метод утилиты для поиска типа. Это лучший подход для меня, поскольку он выглядит аккуратно. Я думаю, если бы вы не использовали Spring, вы могли бы написать свой собственный метод полезности.
import org.springframework.core.GenericTypeResolver;
public abstract class AbstractHibernateDao<T extends DomainObject> implements DataAccessObject<T>
{
@Autowired
private SessionFactory sessionFactory;
private final Class<T> genericType;
private final String RECORD_COUNT_HQL;
private final String FIND_ALL_HQL;
@SuppressWarnings("unchecked")
public AbstractHibernateDao()
{
this.genericType = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), AbstractHibernateDao.class);
this.RECORD_COUNT_HQL = "select count(*) from " + this.genericType.getName();
this.FIND_ALL_HQL = "from " + this.genericType.getName() + " t ";
}
Однако есть небольшая лазейка: если вы определите свой Foo
класс как абстрактный. Это означало бы, что вам нужно создать экземпляр класса следующим образом:
Foo<MyType> myFoo = new Foo<MyType>(){};
(Обратите внимание на двойные фигурные скобки в конце.)
Теперь вы можете получить тип T
во время выполнения :
Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
Обратите внимание, что mySuperclass
должен быть суперклассом определения класса, фактически определяющим конечный тип для T
.
Он также не очень изящный, но вы должны решить, предпочитаете ли вы new Foo<MyType>(){}
или new Foo<MyType>(MyType.class);
в своем коде.
Например:
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.NoSuchElementException;
/**
* Captures and silently ignores stack exceptions upon popping.
*/
public abstract class SilentStack<E> extends ArrayDeque<E> {
public E pop() {
try {
return super.pop();
}
catch( NoSuchElementException nsee ) {
return create();
}
}
public E create() {
try {
Type sooper = getClass().getGenericSuperclass();
Type t = ((ParameterizedType)sooper).getActualTypeArguments()[ 0 ];
return (E)(Class.forName( t.toString() ).newInstance());
}
catch( Exception e ) {
return null;
}
}
}
Затем:
public class Main {
// Note the braces...
private Deque<String> stack = new SilentStack<String>(){};
public static void main( String args[] ) {
// Returns a new instance of String.
String s = stack.pop();
System.out.printf( "s = '%s'\n", s );
}
}
TypeLiteral
– ATG
6 June 2014 в 00:44
a
и b
, созданные таким образом, будут расширять один и тот же класс, но не иметь одинаковые классы экземпляров. a.getClass() != b.getClass()
– Martin Serrano
20 March 2015 в 18:04
Стандартный подход / обходное решение / решение заключается в добавлении объекта class
в конструктор (ы), например:
public class Foo<T> {
private Class<T> type;
public Foo(Class<T> type) {
this.type = type;
}
public Class<T> getType() {
return type;
}
public T newInstance() {
return type.newInstance();
}
}
public <T> T yourMethodSignature(Class<T> type) {
// get some object and check the type match the given type
Object result = ...
if (type.isAssignableFrom(result.getClass())) {
return (T)result;
} else {
// handle the error
}
}
Если вы расширяете или реализуете какой-либо класс / интерфейс, который использует общие элементы, вы можете получить общий тип родительского класса / интерфейса без изменения любого существующего класса / интерфейса вообще.
три варианта,
Случай 1 Когда ваш класс расширяет класс, который использует Generics
public class TestGenerics {
public static void main(String[] args) {
Type type = TestMySuperGenericType.class.getGenericSuperclass();
Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
for(Type gType : gTypes){
System.out.println("Generic type:"+gType.toString());
}
}
}
class GenericClass<T> {
public void print(T obj){};
}
class TestMySuperGenericType extends GenericClass<Integer> {
}
Случай 2 Когда ваш класс реализует интерфейс, который использует Generics
public class TestGenerics {
public static void main(String[] args) {
Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
for(Type type : interfaces){
Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
for(Type gType : gTypes){
System.out.println("Generic type:"+gType.toString());
}
}
}
}
interface GenericClass<T> {
public void print(T obj);
}
class TestMySuperGenericType implements GenericClass<Integer> {
public void print(Integer obj){}
}
Случай 3 Когда ваш интерфейс расширяет интерфейс, который использует Generics
public class TestGenerics {
public static void main(String[] args) {
Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
for(Type type : interfaces){
Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
for(Type gType : gTypes){
System.out.println("Generic type:"+gType.toString());
}
}
}
}
interface GenericClass<T> {
public void print(T obj);
}
interface TestMySuperGenericType extends GenericClass<Integer> {
}
На самом деле, я полагаю, у вас есть поле в вашем классе типа T. Если нет поля типа T, в чем смысл иметь общий тип? Итак, вы можете просто сделать instanceof в этом поле.
В моем случае у меня есть
List<T> items;в моем классе, и я проверяю, является ли тип класса «Locality»
if (items.get(0) instanceof Locality) ...
Конечно, это работает только в том случае, если общее число возможных классов ограничено.
Вы не можете этого сделать из-за стирания стилей. См. Также вопрос о переполнении стека Java generics - стирание типа - когда и что происходит .
Представьте, что у вас есть абстрактный суперкласс, который является общим:
public abstract class Foo<? extends T> {}
И тогда у вас есть второй класс, который расширяет Foo с помощью общего бара, который расширяет T:
public class Second extends Foo<Bar> {}
Вы можете получить класс Bar.class
в классе Foo, выбрав Type
(из ответа bert bruynooghe) и вызывая его с помощью экземпляра Class
:
Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
//Parse it as String
String className = tType.toString().split(" ")[1];
Class clazz = Class.forName(className);
Вы должны отметить эту операцию не является идеальным, поэтому рекомендуется кэшировать вычисленное значение, чтобы избежать множественных вычислений. Одно из типичных применений - это общая реализация DAO.
Окончательная реализация:
public abstract class Foo<T> {
private Class<T> inferedClass;
public Class<T> getGenericClass(){
if(inferedClass == null){
Type mySuperclass = getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
String className = tType.toString().split(" ")[1];
inferedClass = Class.forName(className);
}
return inferedClass;
}
}
Возвращаемое значение - Bar.class при вызове из класса Foo в другой функции или из класса Bar.
toString().split(" ")[1]
, что было проблемой, во избежание "class "
– IgniteCoders
10 May 2016 в 19:09
Я нашел общий и простой способ сделать это. В моем классе я создал метод, который возвращает общий тип в соответствии с его положением в определении класса. Давайте предположим определение класса следующим образом:
public class MyClass<A, B, C> {
}
Теперь давайте создадим некоторые атрибуты для сохранения типов:
public class MyClass<A, B, C> {
private Class<A> aType;
private Class<B> bType;
private Class<C> cType;
// Getters and setters (not necessary if you are going to use them internally)
}
Затем вы можете создать общий метод, который возвращает тип на основе по индексу общего определения:
/**
* Returns a {@link Type} object to identify generic types
* @return type
*/
private Type getGenericClassType(int index) {
// To make it use generics without supplying the class type
Type type = getClass().getGenericSuperclass();
while (!(type instanceof ParameterizedType)) {
if (type instanceof ParameterizedType) {
type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
} else {
type = ((Class<?>) type).getGenericSuperclass();
}
}
return ((ParameterizedType) type).getActualTypeArguments()[index];
}
Наконец, в конструкторе просто вызовите метод и отправьте индекс для каждого типа. Полный код должен выглядеть так:
public class MyClass<A, B, C> {
private Class<A> aType;
private Class<B> bType;
private Class<C> cType;
public MyClass() {
this.aType = (Class<A>) getGenericClassType(0);
this.bType = (Class<B>) getGenericClassType(1);
this.cType = (Class<C>) getGenericClassType(2);
}
/**
* Returns a {@link Type} object to identify generic types
* @return type
*/
private Type getGenericClassType(int index) {
Type type = getClass().getGenericSuperclass();
while (!(type instanceof ParameterizedType)) {
if (type instanceof ParameterizedType) {
type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
} else {
type = ((Class<?>) type).getGenericSuperclass();
}
}
return ((ParameterizedType) type).getActualTypeArguments()[index];
}
}
Вот рабочее решение:
@SuppressWarnings("unchecked")
private Class<T> getGenericTypeClass() {
try {
String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
Class<?> clazz = Class.forName(className);
return (Class<T>) clazz;
} catch (Exception e) {
throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
}
}
ПРИМЕЧАНИЯ: Может использоваться только как суперкласс
Child extends Generic<Integer>
) OR
new Generic<Integer>() {};
) Я использую обходное решение для этого:
class MyClass extends Foo<T> {
....
}
MyClass myClassInstance = MyClass.class.newInstance();
У меня была эта проблема в абстрактном родовом классе. В этом конкретном случае решение проще:
abstract class Foo<T> {
abstract Class<T> getTClass();
//...
}
, а затем производного класса:
class Bar extends Foo<Whatever> {
@Override
Class<T> getTClass() {
return Whatever.class;
}
}
Возможно:
class Foo<T> {
Class<T> clazz = (Class<T>) DAOUtil.getTypeArguments(Foo.class, this.getClass()).get(0);
}
Вам нужны две функции из svn / trunk / dao / src / main / java / com / googlecode / genericdao / dao / DAOUtil.java .
Дополнительные пояснения см. в Отражение дженериков .
Как объяснялось в других ответах, чтобы использовать этот подход ParameterizedType
, вам нужно расширить класс, но это похоже на дополнительную работу, чтобы сделать весь новый класс, который расширяет его ...
So , что делает класс абстрактным, он заставляет вас расширять его, тем самым удовлетворяя требование подкласса. (используя lombok's @Getter).
@Getter
public abstract class ConfigurationDefinition<T> {
private Class<T> type;
...
public ConfigurationDefinition(...) {
this.type = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
...
}
}
Теперь, чтобы расширить его, не определяя новый класс. (Обратите внимание, что {} на конце ... расширен, но ничего не перезаписывайте - если вы не хотите).
private ConfigurationDefinition<String> myConfigA = new ConfigurationDefinition<String>(...){};
private ConfigurationDefinition<File> myConfigB = new ConfigurationDefinition<File>(...){};
...
Class stringType = myConfigA.getType();
Class fileType = myConfigB.getType();
Лучше, чем класс, предложенный другими, - передать объект, который может сделать то, что вы сделали бы с классом, например, создать новый экземпляр.
interface Factory<T> {
T apply();
}
<T> void List<T> make10(Factory<T> factory) {
List<T> result = new ArrayList<T>();
for (int a = 0; a < 10; a++)
result.add(factory.apply());
return result;
}
class FooFactory<T> implements Factory<Foo<T>> {
public Foo<T> apply() {
return new Foo<T>();
}
}
List<Foo<Integer>> foos = make10(new FooFactory<Integer>());
У меня есть (уродливое, но эффективное) решение для этой проблемы, которое я использовал недавно:
import java.lang.reflect.TypeVariable;
public static <T> Class<T> getGenericClass()
{
__<T> ins = new __<T>();
TypeVariable<?>[] cls = ins.getClass().getTypeParameters();
return (Class<T>)cls[0].getClass();
}
private final class __<T> // generic helper class which does only provide type information
{
private __()
{
}
}