Создание экземпляра внутреннего класса

#java #inner-classes

#java #внутренние классы

Вопрос:

Я работаю над типовой проблемой переопределения хэш-кода и метода equals, но получаю сообщение об ошибке: «Не доступен заключающий экземпляр типа CustomHashCodeExample. Должно быть указано выделение с включающим экземпляром типа CustomHashCodeExample (например, x.new A() где x является экземпляром CustomHashCodeExample).» Я написал внутренний класс HashPerson, и я получаю эту ошибку, когда пытаюсь создать экземпляр этого внутреннего класса в другом методе, называемом testHashCodeOverride().

 public static void testHashCodeOverride(){   
    System.out.println("nTest HashCode Override Method");
    System.out.println("==================================n");

    HashPerson william = new HashPerson("willy");
    HashPerson bill = new HashPerson("willy");          
}
  

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

 public class HashCodeExample {

    public static void testHashCodeOverride() {

        HashPerson william = new HashPerson("Willy");
        HashPerson bill = new HashPerson("Willy");
        System.out.println("Hash code for william  = "   william.hashCode());
        System.out.println("Hash code for bill     = "   bill.hashCode());

        HashMap table = new HashMap();
        table.put(william, "Silly");

        if (table.containsKey(william)) {
            System.out.println(table.get(william));
        } else {
            System.out.println("Key "   william   " not found");
        }

        if (table.containsKey(bill)) {
            System.out.println(table.get(bill));
        } else {
            System.out.println("Key "   bill   " not found");
        }


    }

    class HashPerson {
        private static final int HASH_PRIME = 1000003;

        public HashPerson(String name) {
            this.name = name;
        }

        public String toString() {
            return name;
        }

        public boolean equals(Object rhs) {
            if (this == rhs)
                return true;

            // make sure they are the same class
            if (rhs == null || rhs.getClass() != getClass())
                return false;

            // ok, they are the same class. Cast rhs to HashPerson
            HashPerson other = (HashPerson) rhs;

            // our test for equality simply checks the name field
            if (!name.equals(other.name)) {
                return false;
            }

            // if we get this far, they are equal
            return true;
        }
        public int hashCode() {
            int result = 0;
            result = HASH_PRIME * result   name.hashCode();
            return resu<
        }
        private String name;

    }
}
  

Комментарии:

1. Вам действительно следует очистить форматирование кода примера и сократить его до самого необходимого. В его текущем состоянии это в значительной степени нечитаемо.

2. Вы можете упростить свой hashCode метод, просто return name.hashCode();

3. @Jonik: Спасибо за очистку форматирования кода. У меня пока недостаточно репутации, чтобы сделать это. Однако это немного сбивает меня с толку. Когда я смотрю на код, он должен выдавать ту же ошибку «без включающего экземпляра», потому что статический метод testHashCodeOverride хочет создать экземпляр объекта нестатического вложенного (= внутреннего) класса HashPerson .

4. @Steve, это пример, который показывает, как переопределить hashcode для всех сценариев, я просто пробовал то же самое на моем собственном. name.hashCode(); вернет мне адрес памяти, правильно?

5. @t3ch name.hashCode() возвращает хэш-код имени. Поскольку name является строкой, хэш основан на тексте строки.

Ответ №1:

Я думаю, вы хотите объявить HashPerson класс как static . В противном случае он может быть создан только в контексте содержащего класса, либо в методе содержащего класса, либо с использованием кода, подобного этому:

 ContainingClass container = new ContainingClass();
HashPerson william = container.new HashPerson("willy");
  

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

Комментарии:

1. Спасибо MForster, место, в котором я запутался, у меня есть пример программы, которая написана точно так же, как я делаю это здесь, и в этом коде внутренний класс не является статическим, и они также не создают экземпляр содержащего класса. Я что-то упускаю в этом коде? «Добавление кода в мой вопрос»

2. Создают ли они экземпляр внутри метода содержащего его класса?

3. Возможно, я неправильно читаю код. Только что опубликовал фактический код.

4. >> потому что нестатические вложенные классы (называемые внутренними классами) всегда содержат неявную ссылку на содержащий объект. MForster, почему для внутреннего класса плохо иметь ссылку на содержащий класс?

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

Ответ №2:

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

Нестатические члены класса (переменные, методы, внутренние классы) находятся в каждом экземпляре класса. Поэтому при доступе к нестатическим членам из статического контекста (например, статического метода, подобного testHashCodeOverride ) вам необходимо указать экземпляр заключающего класса.

Комментарии:

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

2. Не используйте внутренние классы для общедоступных API. Были добавлены внутренние классы, чтобы иметь замыкания и анонимные классы.

3. По умолчанию классы верхнего уровня статичны, но внутренние классы нестатичны. Если вы создаете внутренний класс только для целей организации кода, то предпочтительнее создать его static и рассматривать как отдельный класс (с возможностью ограничения видимости). Если вам нужно, чтобы внутренний класс имел доступ к нестатическим членам своего окружающего класса, тогда вам следует сделать его нестатическим.

Ответ №3:

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

  1. Статический внутренний класс : когда внутренний класс статичен, допустим, код выглядит так, как описано.

     class OuterClass 
    {
        static int outer_x = 10;
        int outer_y = 20;
        // static nested class 
        static class StaticNestedClass {
            void display() {
    
            }
        }
    }
    
    OuterClass.StaticNestedClass nestedObject =
     new OuterClass.StaticNestedClass();
      

или просто этого может быть достаточно для статических классов

  new StaticNestedClass();
  
  1. Локальные внутренние классы (нестатические): внутренние классы, которые не являются статическими. Одним из преимуществ локальных внутренних классов является то, что они могут получать доступ ко всем закрытым элементам данных закрытого класса.

     OuterClass outerObject = new OuterClass(); 
    OuterClass.InnerClass innerObject = outerObject.new InnerClass();
      
  2. Анонимный внутренний класс (реализующий интерфейс) : Этот тип классов обычно скрыт, невозможно напрямую увидеть блок Class{…} в коде, поэтому он известен как анонимный внутренний класс. Здесь описывается, как создать его экземпляр в случае, если внутренний класс реализует интерфейс, доступный для выполнения.

     Runnable r = new Runnable() 
    { 
      //This is Anonymous Class
      public void run() {
        System.out.println("Child Thread");
      }
    };
      
  3. Анонимный внутренний класс (расширение одного класса): у нас может быть анонимный внутренний класс, который расширяет класс, здесь я беру пример класса Thread,

     Thread t = new Thread(new Runnable() 
    {
      //Anonymous Inner class
    public void run() {
        System.out.println("Child Thread");
    }
    });
      
  4. Анонимный внутренний класс, который определяет внутренний аргумент метода / конструктора : Внутренние классы также могут быть определены внутри методов, здесь приведен пример, как мы можем определить и создать его экземпляр в аргументе

     public static void main(String[] args) 
    { 
    //Here we are using Anonymous Inner class 
    //that define inside argument, here constructor argument 
    Thread t = new Thread(new Runnable() 
    { 
        public void run() 
        { 
            System.out.println("Child Thread"); 
        } 
    });
    t.start();