IDisposable метафора в Java?

Как разработчик java, входящий в .NET, я хотел бы понять интерфейс IDisposable. Может ли кто-нибудь попытаться объяснить это и как он отличается от того, что происходит на Java? Спасибо.

Ответы

Ответ 1

Я написал подробную серию статей о IDisposable.

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

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

Пока GC не запускается и не обрабатывает осиротевший элемент управления, он все равно будет использовать ресурсы, поскольку он все еще удерживает HWND.

IDisposable предоставляет средства для объектов, которые содержат код, который нуждается в очистке отдельно от GC, чтобы быть явно очищенным пользователем объекта. В контрольном случае мы можем вызвать myControl.Dispose(),, который сразу же будет синхронно очищать "собственные" ресурсы (HWND), используемые элементом управления.

Ответ 2

С помощью java 1.7 появляется новая введенная инструкция try-with-resource.

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
}

Используемые здесь объекты должны реализовывать интерфейс AutoCloseable. Это не совсем то же самое, что IDisposable, но close() автоматически вызывается в конце. Это дает возможность реализовать аналогичное поведение.

Приведенный выше код аналогичен

BufferedReader br = new BufferedReader(new FileReader(path));
try {
    return br.readLine();
} finally {
    if (br != null) br.close();
}

Подробнее об этом читайте в java tutorial. Образец кода идет оттуда.

Ответ 3

Бывают ситуации, когда вам нужна надежная утилизация ресурсов, принадлежащих вашему классу. Например, открытое соединение должно быть закрыто в надлежащее время, а не когда GC решает собрать память. В .NET методе Dispose используется для этого соглашение. Его можно вызвать в блоке try ... finally, например:

IConnection conn = OpenConnection();
try{
   ...
}finally{
    conn.Dispose();
}

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

using(IConnection conn = OpenConnection()){
} // Dispose is called at the end.

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

class TimeMeasure: IDisposable{
     public void Measure(string operation) { ... } // recourds operation time
     public void Dispose(){
        ... // print timings for all operations
     }
}

Использование:

using(TimeMeasure m = new TimeMeasure())
{
    DoFoo();
    m.Measure("Foo");
    DoBar();
    m.Measure("Bar");
} // timings for 'Foo' and 'Bar' are printed here

В Java более или менее эквивалентный интерфейс Closeable. Но для его вызовов нет простого синтаксиса.

Как IDisposable должен быть реализован? Это немного сложно:

  • Если вы владеете ресурсами, вам необходимо убедиться, что они могут быть освобождены явным вызовом Dispose или по GC, но не тем и другим. Итак, вам нужен флаг, указывающий на факт удаления. Для уменьшения дублирования кода код удаления перемещается в отдельный метод.

Пример:

bool disposed;
public void Dispose(){
    Dispose(true);
    GC.SuppressFinalize(this); // tell GC not to call Finalizer() for this object
}

~MyObject(){
    Dispose(false);
}

void Dispose(bool manualDisposing){
    if(!disposed){
        disposed = true;
        if(manualDisposing)
           ... // call Dispose on all IDisposable fields
        ... // dispose all native resources
    }
}
  • Если вы не используете ресурсы, как в случае класса TimeMeasure, нет необходимости в Finalizer, вы просто делаете необходимую логику в Dispose.

Ответ 4

В основном IDisposable на высоком уровне в сочетании с ключевым словом using предоставляет синтаксическую поддержку тому, что вы видите как обычную идиому Java, например:

   Connection c = null;
   try {
      c = getConnection();
      ....
   } finally {
      if (c != null) {
          c.close();
      }
   }

Если Java имеет ключевое слово using и интерфейс IDisposable с помощью метода close(), это может выглядеть так:

   //psudo-Java
   using(Connection c = getConnection()) {
      .....
   }

Когда метод close неявно вызывается в конце этого блока. Не нужно попробовать/наконец.

Сказано, что IDisposible имеет довольно сложный контракт и в основном занимается освобождением неуправляемой памяти. Вам нужно много работать с Java, чтобы иметь неуправляемую память (в основном с компонентами JNI и Swing, у вас есть концепция), но это гораздо более распространено в .NET, поэтому для этой концепции существует языковая поддержка.

Ответ 5

IDisposable interface используется для освобождения неуправляемых ресурсов вручную.

Ответ 6

Нет эквивалента.

Он используется, когда вы используете неуправляемые ресурсы (в Java все управляемые области ресурсов).

В .net память, выделяемая управляемыми ресурсами, автоматически собирается GC (как в Java).

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

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