Отримання назви підкласу з суперкласу


78

Скажімо, у мене є базовий клас з ім’ям Entity. У цьому класі у мене є статичний метод отримання імені класу:

class Entity {
    public static String getClass() {
        return Entity.class.getClass();
    }
}

Тепер у мене є ще один клас, який це продовжує.

class User extends Entity {
}

Я хочу отримати ім'я класу User:

System.out.println(User.getClass());

Моя мета - побачити вивід "com.packagename.User" на консолі, але замість цього я отримаю "com.packagename.Entity", оскільки на клас Entity посилаються безпосередньо зі статичного методу.

Якби це не був статичний метод, це можна було б легко вирішити, використовуючи thisключове слово в Entityкласі (тобто:) return this.class.getClass(). Однак мені потрібен цей метод, щоб залишатися статичним. Будь-які пропозиції щодо того, як до цього підійти?

Відповіді:


41

Неможливо. Статичні методи жодним чином не є поліморфними під час виконання. Абсолютно неможливо розрізнити такі випадки:

System.out.println(Entity.getClass());
System.out.println(User.getClass());

Вони компілюються в один і той же байт-код (припускаючи, що метод визначений у Entity).

До того ж, як би ви назвали цей метод таким чином, щоб було логічно, щоб він був поліморфним?


1
Дякую за роз'яснення, мабуть, я недостатньо знаю про те, як компілюються статичні методи. Це допомагає прояснити ситуацію, мені доведеться попрацювати над альтернативним рішенням.
Метт Хаггінс,

2
+1, точно. Все це питання породжується нерозумінням того, як працюють статичні методи.
Mark Peters

94

Не робіть метод статичним. Проблема в тому, що під час виклику getClass()ви викликаєте метод у супер-класі - статичні методи не успадковуються. Крім того, ви в основному займаєтеся переглядом імен Object.getClass(), що бентежить.

Якщо вам потрібно зареєструвати ім'я класу в суперкласі, використовуйте

return this.getClass().getName();

Це поверне "Entity", коли у вас є Entityекземпляр, "User", коли у вас є Userекземпляр тощо.


1
thisне існує в статичному методі.
Matt Huggins

32
@Matt Huggins, ось чому перше речення у моїй відповіді говоритьDon't make the method static
matt b

2
@ matt b - ось чому сказано в останньому реченні мого запитанняHowever, I need this method to remain static.
Matt Huggins

@MattHuggins Мені цікаво, чому метод Pojo не працює. Якщо ви використовуєте статичний метод, вам потрібно вказати ім'я класу, яке вже є в коді. Якщо ви створили екземпляр підкласу, але посилання є на суперклас, у вас є об’єкт, і вам не потрібно викликати статичний метод.
Mindwin

5
Незважаючи на те, що ця відповідь не стосується "Однак мені потрібен цей метод, щоб залишатися статичним", корисно, якщо (як і я) ви приземлилися тут, шукаючи, як отримати клас у методі, який не повинен бути статичним. ;)
Райан Хіткот,

6

Ваше запитання неоднозначне, але, наскільки я можу сказати, ви хочете знати поточний клас із статичного методу. Той факт, що класи успадковують один від одного, не має значення, але заради обговорення я реалізував це також таким чином.

клас Батько {
    public static void printClass () {
      System.out.println (Thread.currentThread (). GetStackTrace () [2] .getClassName ());
    }
}

тест публічного класу продовжує Parent {
    public static void main (String [] args) {
      printClass ();
    }
}

Ой, цей стек-хак брудний. В чому проблема System.out.println(Parent.class.getName());?
whiskeysierra

2
Як я вже говорив, питання Метта неоднозначне, і я надав одну з можливих відповідей. Очевидно, що ваша версія не дає такого ж результату, як мій код, тому немає порівняння - ви говорите про інше рішення. І читання стеку потоків - це не хак, це звичайний спосіб перевірки ієрархії викликів (звичайно, я пропустив перевірку розумності, оскільки це приклад).
Gilead

Це досить кумедний хак, але мало пов’язаний із запитуваною поведінкою. Ця функція друкує ім'я класу, що викликає метод (тобто, якщо видалити extends Parentта змінити виклик Parent.printClass(), він все одно друкує "Тест").
nemetroid

6

Це працює для мене

this.getClass().asSubclass(this.getClass())

Але я не впевнений, як це працює.


3

Суперклас навіть не повинен знати про існування підкласу, а тим більше виконувати операції на основі повністю кваліфікованої назви підкласу. Якщо вам потрібні операції, засновані на точному класі, і не можете виконати необхідну функцію шляхом успадкування, вам слід зробити щось у такому порядку:

public class MyClassUtil
{
    public static String doWorkBasedOnClass(Class<?> clazz)
    {
        if(clazz == MyNormalClass.class)
        {
            // Stuff with MyNormalClass
            // Will not work for subclasses of MyNormalClass
        }

        if(isSubclassOf(clazz, MyNormalSuperclass.class))
        {
            // Stuff with MyNormalSuperclass or any subclasses
        }

        // Similar code for interface implementations
    }

    private static boolean isSubclassOf(Class<?> subclass, Class<?> superclass)
    {
        if(subclass == superclass || superclass == Object.class) return true;

        while(subclass != superclass && subclass != Object.class)
        {
            subclass = subclass.getSuperclass();
        }
        return false;
    }
}

(Неперевірений код)

Цей клас також не знає про власні підкласи, а скоріше використовує Classклас для виконання операцій. Швидше за все, він все ще буде тісно пов’язаний з реалізаціями (як правило, це погано, а якщо не погано, то не особливо добре ), але я думаю, що така структура краще, ніж суперклас, який з’ясовує, якими є всі її підкласи.


2

Чому ви хочете реалізувати власний метод getClass ()? Ви можете просто використовувати

System.out.println(User.class);

Редагувати (щоб трохи докласти): Ви хочете, щоб метод був статичним. У цьому випадку ви повинні викликати метод для класу, ім'я якого класу ви хочете, будь то підклас або супер-клас. Тоді замість дзвінка MyClass.getClass()ви можете просто зателефонувати MyClass.classабо MyClass.class.getName().

Крім того, ви створюєте staticметод з тим самим підписом, що і Object.getClass()метод екземпляра, який не компілюється.


1
Я навмисно написав запитання, щоб воно було дуже оголеним на StackOverflow і перейшло прямо до суті питання. Це насправді тому, що я будую набагато надійніший статичний метод, який використовує ім’я класу будь-якого підкласу, що розширює цей абстрактний базовий клас.
Метт Хаггінс,

1
Здається, ви хочете поліморфізму staticметодами, що неможливо. Крім того, чи можете ви пояснити, чому вищезазначене недостатньо надійне?
samitgaur

Це недостатньо надійно, тому що мені довелося б перевизначити статичний метод у кожному окремому класі успадкування, а не просто мати можливість визначити статичний метод один раз.
Matt Huggins

1
Якщо ви хочете використовувати статику, ви вже знаєте конкретний клас. В чому проблема User.class.getName()?
whiskeysierra

Тому що , якщо я жорстко User.class.getName()в Entityстатичний метод «S, то , що станеться , якщо я розширюю Entityз новим класом називається Profile? Він все одно поверне ім’я класу для користувача, а не ім’я класу для профілю.
Метт Хаггінс,

2
  1. Створіть змінну String-член у суперкласі.
  2. Додайте this.getClass (). GetName () до конструктора, який зберігає значення у змінній String-члені.
  3. Створіть геттер для повернення імені.

Кожного разу, коли розширений клас створюється, його ім'я зберігатиметься в рядку та буде доступним разом із геттером.


0

Статичний метод пов'язаний з класом, а не з конкретним об'єктом.

Поміркуйте, як це могло б працювати, якби було кілька підкласів - наприклад, Адміністратор - це також Сутність. Як би ваш статичний метод Entity, пов’язаний лише з класом Entity, знав, який підклас ви хочете?

Ви можете:

  • Використовуйте існуючий метод getClass ().
  • Передайте аргумент своєму статичному методу getClass () і викличте метод екземпляра для цього об’єкта.
  • Зробіть свій метод нестатичним і перейменуйте його.

0

Якщо я правильно розумію ваше запитання, я думаю, що єдиним способом досягти бажаного є повторна реалізація статичного методу в кожному підкласі, наприклад:

class Entity {
    public static String getMyClass() {
        return Entity.class.getName();
    }
}

class Derived extends Entity {
    public static String getMyClass() {
        return Derived.class.getName();
    }
}

Буде надруковано пакунок. Цілість та пакунок. Виготовлено, як вам потрібно. Брудно, але привіт, якщо це ваші обмеження ...


0

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

class Entity {
    public static void useClass(Class c) {
        System.out.println(c);
        // to do code here
    }
}

class User extends Entity {
}

class main{
    public static void main(String[] args){
        Entity.useClass(Entity.class);
    }
}

0

Мій контекст: Субклас Сутність із підкласами для об’єктів XML. Моє рішення: Створити змінну класу в суперкласі

Class<?> claz;

Тоді в підкласі я б встановив змінну суперкласу в конструкторі

public class SubClass {
   public SubClass() {
     claz = this.getClass();
   }
}

-4

це дуже просто робить

User.getClass (). GetSuperclass ()


2
Це робить протилежне запитуванню. Я хотів назвати підклас всередині суперкласу. Ваша відповідь надає назву суперкласу всередині підкласу.
Matt Huggins
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.