Реализации Service Loader, требующие открытых конструкторов по умолчанию

Я пытался понять, как в Java Service Loader работает? Я наткнулся на этот блог:

Не могли бы вы помочь мне понять, почему автор утверждает:

Реализация должна иметь открытый конструктор без параметров.

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

Рассмотрим документ Java:

Это говорит:

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

Значит ли это, что я не могу вывести внешний класс из себя? Пожалуйста, рассмотрите следующий код:

import java.io.*;

class Y {
  class ABC {
    ABC() {
      System.out.println("ABC Constructor");
    }
    public void writeExternal(ObjectOutput out)
      throws IOException {
      System.out.println("ABC.writeExternal");
    }
    public void readExternal(ObjectInput in)
      throws IOException, ClassNotFoundException {
      System.out.println("ABC.readExternal");
    }
  }

  public void foo() throws IOException, ClassNotFoundException {
    System.out.println("Constructing objects:");
    ABC abc = new ABC();
    ObjectOutputStream o = new ObjectOutputStream(
    new FileOutputStream("InnerClass.out"));
    System.out.println("Saving objects:");
    o.writeObject(abc);
    o.close();
    // Now get them back:
    ObjectInputStream in = new ObjectInputStream(
      new FileInputStream("InnerClass.out"));
    System.out.println("Recovering abc:");
    // OOPS! Throws an exception:
    abc = (ABC)in.readObject();
  }
}

public class InnerClass {
  public static void main(String[] args) throws IOException, ClassNotFoundException {
    System.out.println("Hello World\n");
    Y y = new Y();
    System.out.println(y);
    y.foo();
  }  
}

Это терпит неудачу во время выполнения:

Hello World

Y@6bc7c054
Constructing objects:
ABC Constructor
Saving objects:
Exception in thread "main" java.io.NotSerializableException: Y$ABC
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1180)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:346)
    at Y.foo(InnerClass.java:24)
    at InnerClass.main(InnerClass.java:40)

Как я могу использовать Externalize class ABC?

Спасибо

3 ответа

Решение

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

Я думаю, что здесь соответствующий код, строка 362:

S p = service.cast(Class.forName(cn, true, loader).newInstance());

Таким образом, он вызывает конструктор без параметров, так как Class.newInstance() может вызвать только конструктор с нулевым аргументом.

Редактировать:

На самом деле, не требуется создавать конструктор с нулевым аргументом для public class если вы не определите другой конструктор.

Javadoc:

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

Итак, следующий код совершенно корректен:

public final class MyServiceImpl implements MyService { 
    @Override
    public long getTime() {
        return System.currentTimeMillis();
    } 
}

Конструктор по умолчанию для MyServiceImpl будет таким же как:

public MyServiceImpl() { }

поскольку MyServiceImpl это публичный класс:

если класс объявлен как открытый, то конструктор по умолчанию неявно получает модификатор доступа public

Таким образом, конструктор в MyServiceImpl избыточно

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


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

Вы можете прочитать причину на официальном документе Java

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

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

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

Заметным исключением из этого является механизм сериализации, который не требует конструктора без параметров, но это только потому, что он имеет некоторую добавленную "магическую помощь" от ВМ.

Другие вопросы по тегам