2009-02-03 1 views
0
public class ExampleClass { 
    public static void main(String[] args) { 
     // TODO Auto-generated method stub 
     Horse hr1 = new Horse(); 
     Horse hr2 = new Horse(); 
     Horse hr3 = new Horse(); 
     Horse hr4 = new Horse(); 
     Set hrSet = new HashSet(); 
     hrSet.add(hr1); 
     hrSet.add(hr2); 
     hrSet.add(hr3); 
     hrSet.add(hr4); 
     Horse hr; 
     String hor = "sher_pkg.Horse"; 
     callHorse(hrSet,hor); 
    } 
    public static void callHorse(Set xSet,String clsName){ 
     try { 
      Class hrt = Class.forName(clsName); 

      Iterator hritr = xSet.iterator(); 
      while(hritr.hasNext()){ 
       exam(hrt.cast(hritr.next())); 
      } 
     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
     } 
    } 
    public static void exam(Object obj){ //I want to use exam(Horse hrr) 
     System.out.println(obj); 
    } 
} 

Здесь аргумент для функции экзамена - Object. Но я хочу, чтобы аргумент был Horse ... так что нужно сделать в вызове метода «exam(hrt.cast(hritr.next()))»? Я не хочу явно использовать имя класса Horse в callHorse() ... Так что же мне делать?Кастинг до неизвестного типа, когда название класса как строка

Спасибо

ответ

3

Примечание: код с последовательностями «if (x instanceof MyClass)» обычно указывает, что вы недостаточно используете полиморфизм. Код, как правило, может быть реорганизован, чтобы избавиться от необходимости проверить это. Но я проигнорирую это, чтобы ответить на заданный вопрос.

Вы можете делать то, что вы пытаетесь сделать, но не без каких-либо изменений кода. Перегрузка метода не может делать то, что вам нужно, потому что в Java перегрузка метода определяется во время компиляции. Таким образом, если у вас есть два метода в классе, где оба метода имеют одинаковое имя, одинаковый тип возвращаемого значения, но разные типы параметров, тогда любой код, вызывающий этот перегруженный метод, должен указывать, какой из них будет вызываться. Ваш текущий код делает это с теми типами, которые он предоставляет из-за использования явных приведений, но полностью динамическая версия этого не делает. Если перегрузка метода была определена во время выполнения, то ваш код будет делать то, что вы хотите. Но поскольку во время компиляции решено, ваш код не компилируется.

Чтобы решить вашу проблему, вы можете использовать дженерики, или вы можете перестроить свой код. Сначала я познакомлю тестовую, которая показывает очень упрощенную версию того, что вы начинаете с:

public class Test { 
    public void test(Object obj) { 
    if (obj instanceof Horse) { 
     Horse c = (Horse) obj; 
     noise(c); 
    } 
    if (obj instanceof Cow) { 
     Cow c = (Cow) obj; 
     noise(c); 
    } 
    } 

    public void noise(Horse h) { 
    System.out.println("Neigh"); 
    } 

    public void noise(Cow c) { 
    System.out.println("Moo"); 
    } 

    public static void main(String[] args) { 
    Object o1 = new Horse(); 
    Object o2 = new Cow(); 
    Test tester = new Test(); 
    tester.test(o1); 
    tester.test(o2); 
    } 
} 

class Horse {} 

class Cow {} 

Этот код работает и делает то, что можно было бы ожидать. Он печатает «Neigh», а затем «Moo».

Вы пытаетесь заменить

if (obj instanceof Horse) { 
     Horse c = (Horse) obj; 
     noise(c); 
    } 

с

if (obj instanceof Horse) { 
     handleNoise(obj, Horse.class); 
    } 

, а затем добавить метод для обработки его (упрощенно):

void handleNoise(Object obj, Class clazz) { 
    noise(clazz.cast(obj)); 
} 

и, как я уже говорил, не работает, перегрузка noise определяется во время компиляции. Компилятор видит, что вы выполняете кастинг, но во время компиляции не знаете, что такое тип. Поэтому он не может выбрать перегрузку и компиляцию.

Лучший способ решить это с помощью полиморфизма, поскольку полиморфизм определяется во время выполнения. То есть, все эти классы реализуют некоторый интерфейс, а затем перемещают рассматриваемый код в отдельные классы. Вот пример, который делает это:

public class Test { 
    public void test(Animal obj) { 
    obj.noise(); 
    } 

    public static void main(String[] args) { 
    Animal o1 = new Horse(); 
    Animal o2 = new Cow(); 
    Test tester = new Test(); 
    tester.test(o1); 
    tester.test(o2); 
    } 
} 

interface Animal { 
    void noise(); 
} 

class Horse implements Animal { 
    public void noise() { 
    System.out.println("Neigh"); 
    } 
} 

class Cow implements Animal { 
    public void noise() { 
    System.out.println("Moo"); 
    } 
} 

Обратите внимание, насколько проще метод тестирования!Если вы можете иметь каждый элемент реализовать интерфейс, который обрабатывает то, что вы называете stringProp ниже, то вы можете упростить часть пути:

if (obj instanceof Cust) { 
    loopOverSet(c.getCustPhonSet()); 
} else if (obj instanceof Name) { 
    loopOverSet(c.getCustNameSet()); 
} 
// and so on for the rest... 

, а затем добавить метод:

void loopOVerSet(Set cxSet) { 
    if (cxSet != null && cxSet.size() > 0) { 
    Iterator cxSetIterator = cxSet.iterator(); 
    while (cxSetIterator.hasNext()) 
    { 
     ((StringProp)cxSetIterator.next()).stringProp(); 
    } 
    } 
} 

Это предполагает, что previously- перегруженные методы stringProp были перемещены в отдельные классы CustPhone и CustName и т. д. и что эти классы реализуют некоторый интерфейс, который я назвал StringProp, где этот интерфейс определяет метод stringProp(). Поскольку этот код использует , переопределяя вместо перегрузкой, это будет определено во время выполнения.

1

Возможно, вы захотите ознакомиться с дженериками.

public static void callHorse(Set<Horse> xSet) { 
    Iterator<Horse> hritr = xSet.iterator(); 
    while (hritr.hasNext()) { 
     exam(hritr.next()); 
    } 
} 
public static void exam(Horse obj) { //I want to use exam(Horse hrr) 
    System.out.println(obj); 
} 

Конечно, в вашем примере вы всегда можете просто бросить объекты. Почему вы не хотите этого делать, это вне меня.

0

Вы можете явно привести в вызове функции -

try { 
      Class hrt = Class.forName(clsName); 

      Iterator hritr = xSet.iterator(); 
      while(hritr.hasNext()){ 
        exam((Horse)hrt.cast(hritr.next())); 
      } 
    } 

, но я не совсем уверен, что вы пытаетесь достичь здесь - Если вы пишете код, который явно ссылается на лошадей, почему вы нужно динамически определять тип класса из строки?

0

Прежде всего, ваш набор должен использовать либо дженерики, либо явно определенные как удерживающие объекты Horse.

(final Set xSet<Horse>, final String clsName){ 
...} 

Исправьте это, и вы устранили 90% проблем.

0

Похоже, что ваш дизайн неправильный для Java, и вы не можете напрямую делать то, о чем просите.

Возможно, вам нужно изменить свой код, чтобы использовать шаблон посетителя? В противном случае вам нужно объяснить свое требование вместо решения, которое вы хотите использовать. Таким образом, мы можем рассказать вам о правильных решениях Java для вашего требования.

+0

Код, который я написал, должен был объяснить, что я хотел. В любом случае, я хочу набирать вывод из набора. Но имя класса, используемое для typecast, получает динамически, то есть (clsname) hritr.next() вместо (Horse) hritr.next() .. где clsname = «Лошадь» .. Я думаю, что у меня появилась моя точка зрения ... Я не могу использовать общий набор ... – msher420

+0

Нет, я еще не понял вашу точку зрения. Почему динамическое имя класса? Имеют ли эти классы общий базовый класс? Все возможные имена классов известны во время компиляции? – Darron

+0

Фу! кажется, что мы не можем динамически придумать объект .. в любом случае посмотрим последнее сообщение, которое я добавил .. Извините, если я тупой .. очень любитель, пытаясь экипировать себя ... спасибо – msher420

0

Я не уверен, что возможно или желательно избегать ссылки на «Лошадь» в методе callHorse. Судя по printstacktrace после ClassNotFoundException, вы бросаете жесткую ошибку, если класс по какой-либо причине не найден.

Не могли бы вы по той же причине просто бросить «Лошадь», а затем поймать classcastexception, если что-то в Set не является лошадью?

Можете ли вы объяснить, почему именно вам нужно передать имя класса вместо класса?

Возможно, вы также можете использовать перегрузку метода, но мне пришлось бы проверить это, потому что я не совсем уверен, что в этом случае приоритет.

1

Когда вы говорите:

exam(Horse hrr) 

вы сообщаете компилятор, что вы хотите, чтобы проверить, все вызовы на экзамен() и убедитесь в том, что каждый вызов предоставляет объект лошади в качестве аргумента. Тем не менее, в callHorse() вы вызываете exam() с динамическим аргументом, и компилятор не имеет возможности проверить аргумент.

Возможно, вы могли бы обойти это, используя отражение и динамическое обращение к методу exam().

0

Если вы делаете динамическое приведение с использованием Class.cast() с аргументом, который вы передаете другой функции, то во время компиляции ничего неизвестно о том, какой тип вы передаете. Вот почему вы не можете использовать Horse в качестве типа аргумента, где вы определяете метод, но затем вызываете метод с использованием отражения так, как вы. Ваш актерский состав делает очень мало, за исключением проверки того, что - пока вы не получите Exception - набор, который вы проходите, целиком состоит из членов Class, которые вы передаете от имени.

Обратите внимание, что метод Class.cast() был введен в Java 5, то есть у вас есть доступ к Generics, если у вас есть доступ к Class.cast(). Дженерики могут помочь очистить вещи, хотя они не будут решать проблему, которую вы пытаетесь решить.

Использование цикла Java 5 for вы можете переписать цикл следующим образом:

public static void callHorse(Set<?> xSet, String clsName) { 
    try { 
     Class<?> hrt = Class.forName(clsName); 
     for (Object x : xSet) { 
     exam(hrt.cast(x)); 
     } 
    } catch (ClassNotFoundException e) { 
     e.printStackTrace(); 
    } 
    } 

Эта версия менее загроможден и делает бросок более очевидной. Вы бросаете совершенно произвольный тип. Литой может быть любой тип, если определение класса можно загрузить из пути к классам. Таким образом, если ваш метод exam() принимает аргумент Horse, тогда компилятор знает, что он не может гарантировать, что вызов будет успешным, и код не скомпилируется.

Даже если вы попытаетесь перегрузить, это не сработает. То есть, если вы создаете методы:

public static void exam(Object obj) { 
    System.out.println("Object " + obj); 
} 

public static void exam(Horse obj) { 
    System.out.println("Horse " + obj); 
} 

метод exam(Object) будет всегда быть один вызываются. Попробуй.

Суть в том, что то, что вы пытаетесь сделать, не может быть выполнено. Вы должны предоставить нам больше информации о том, какова ваша цель, прежде чем мы сможем вам помочь.

+0

Код, который я написал, должен был объяснить, что я хотел. В любом случае, я хочу набирать вывод из набора. Но имя класса, используемое для typecast, получает динамически ie (clsname) hritr.next() вместо (Horse) hritr .next() .. где clsname = «Лошадь» .. Я думаю, что у меня появилась моя точка зрения ... Я не могу использовать общий набор в Set ... – msher420

+0

@ msher420: * Почему вы хотите вывести результат набора ? Какова цель этого актера? Не зная этого, вам трудно помочь. – Eddie

+0

9 Плехе !! кажется, что мы не можем динамически придумать объект .. в любом случае посмотрим на последнее сообщение, добавленное мной .. Извините, если я тупой .. очень любитель, пытаясь экипировать себя ... спасибо ... – msher420

0

Почему бы не написать это так? Каковы ваши требования?

public static void main(String[] args) { 
    Set<Horse> horses = new HashSet<Horse>(); 
    horses.add(new Horse()); 
    horses.add(new Horse()); 
    horses.add(new Horse()); 
    horses.add(new Horse()); 

    callHorse(horses); 
} 

public static void callHorse(Set<Horse> horses) { 
    for (Horse horse : horses) { 
     exam(horse); 
    } 
} 

public static void exam(Horse horse) { 
    System.out.println(horse); 
} 

В зависимости от того, что вы делаете в методе экзамен(), это также может иметь смысл сделать это метод экземпляра Лошадь, как это:

public static void main(String[] args) { 
    Set<Horse> horses = new HashSet<Horse>(); 
    horses.add(new Horse()); 
    horses.add(new Horse()); 
    horses.add(new Horse()); 
    horses.add(new Horse()); 

    examineHorses(horses); 
} 

public static void examineHorses(Set<Horse> horses) { 
    for (Horse horse : horses) { 
     horse.examine(); 
    } 
} 

// in Horse.java 
public class Horse { 
    public void examine() { 
     System.out.println(this); 
    } 
    ... 
} 
+0

Я почти написал ответ точно так же, как что вы опубликовали, но я думаю, что часть требования должна включать использование рефлексии. – Eddie

0

Ваша реальная цель, чтобы иметь несколько версии метода exam(), которые принимают разные типы в качестве параметров и динамически выбирают версию, необходимую во время выполнения?

Вы можете сделать это явно с отражением. Вот пример программы.

import java.lang.reflect.*; 

public class Test { 

public static void exam(Object o) { 
    System.out.println("Object version called"); 
} 


public static void exam(Test t) { 
    System.out.println("Test version called"); 
} 

public static void main (String[] args) { 

try { 

    // Create an instance of Test but reference it as an Object 

    Object untypedTest = new Test(); 

    // Calling exam directly will invoke the Object version 

    exam(untypedTest); 

    // But if we use reflection to select the version of exam 
    // that takes the desired class name, we can invoke it without 
    // even explicitly casting 

    String className = "Test"; 

    Class[] examMethodParams = { Class.forName(className) }; 

    Method examMethod = Test.class.getMethod("exam", examMethodParams ); 

    Object[] actualParams = { untypedTest }; 

    examMethod.invoke(null, actualParams); 

} catch (Exception e) { 
    e.printStackTrace(); 
} 

} 

} 
0

HI,

После поиска через я обнаружил, что динамическое приведение во время выполнения не может быть выполнена. Поэтому то, что я пытаюсь понять, кажется абсурдным.

Я пытался уменьшить цикломатическую сложность метода1. Я пытался создать метод2, который содержит обобщенный шаблон шаблона повторения, найденный в методе 1, и вызывая метод2 из метода1, где это необходимо ...

шаблон был похож на первый метод ..

if (obj instanceof Cust) 
{ 
    Cust c = (Cust) obj; 
    Set cxSet = c.getCustPhonSet(); 
    CustPhon cx; 
    if (cxSet != null && cxSet.size() > 0) 
    { 
     Iterator cxSetIterator = cxSet.iterator(); 
     while (cxSetIterator.hasNext()) 
     { 
      cx = (CustPhon) cxSetIterator.next(); 
      this.stringProp(cx); 
     } 
    } 
    //....pattern continues here... CustPhon is replaced by various classes like CustNam etc... Also getCustPhonSet by getCustNamSet etc... 
} 

так я думал, писать обобщенный метод для приведенного выше рисунка, как это ::

public void dynamicIteration(Set xlSet, String clsName) 
{ 
    if (xSet != null && xSet.size() > 0) 
    { 
     try{ 
      Class clsinstance = Class.forName(clsName); 
      Iterator itr = generalSet.iterator(); 
      while(itr.hasNext()) 
      { 
       this.stringProp(clsinstance.cast(itr.next()));// See this is wrong.. thats y i posted here by using a simple Horse example 
      } 
     }catch(ClassNotFoundException e) 
     { 
      e.printStackTrace(); 
     } 
    } 
} 

Вызов method2 из метода 1

//process customer email address 
Set cxSet = c.getCustPhonSet(); 
className = "pkg.CustPhon"; 
dynamicIteration(cxSet,className); 
// Similarly for other patterns 

К таким образом я должен быть в состоянии уменьшить цикломатическую сложность

Это то, что я пытался сделать.

0

Эй, я думаю, это решит проблему. Вам нужно решить, какой объект он есть, чтобы u мог вызвать соответствующую операцию, правильно ???

Поскольку мы используем переопределяющие элементы, можно достичь самой необходимой функциональности.

Что может быть самым подходящим решением для этого, думаю, Эдди?

YOu переопределяет метод в соответствующих классах, так что при вызове он переходит в метод corressponding.

У?

 Смежные вопросы

  • Нет связанных вопросов^_^