2017-06-15 3 views
2

Ich muss N Multicast-Sockets öffnen (wobei N aus der Größe einer Argumentliste stammt). Ich werde dann die gleichen Daten an jede der N Buchsen innerhalb einer Schleife senden, und schließe schließlich jeden Sockel. Meine Frage ist, wie mache ich das mit dem Try-with-Resources-Block? Hier finden Sie, wie ich dies zu tun, mit einer einzigen Ressource:Java Try-With-Resources Unbekannte Ressource Count

final int port = ...; 
try (final MulticastSocket socket = new MulticastSocket(port)) { 
    // Do a bunch of sends of small packet data over a long period of time 
    ... 
} 

Der einzige Weg, ich denke, kann dies mit mehreren Ports zu tun, ist die folgende:

final List<Integer> ports = ...; 
final List<MulticastSocket> sockets = new ArrayList<>(ports.size()); 
try { 
    for (final Integer port : ports) { 
     sockets.add(new MulticastSocket(port)); 
    } 

    // Do a bunch of sends of small packet data over a long period of time 
    ... 
} finally { 
    for (final MulticastSocket socket : sockets) { 
     try { 
      socket.close(); 
     } catch (final Throwable t) { 
      // Eat the exception 
     } 
    } 
} 

Gibt es eine prägnante Art und Weise um dies zu erreichen, oder ist meine vorgeschlagene Lösung so gut wie es geht?

Antwort

0

durch die von Mike Nakis vorgeschlagene Idee inspiriert, kam ich mit der folgenden Klasse bis ...

package myNamespace; 

import java.util.ArrayList; 
import java.util.Collection; 
import java.util.Iterator; 
import java.util.List; 
import java.util.ListIterator; 

import myNamespace.ThrowingFunction; 
import myNamespace.ThrowingSupplier; 

/** Collection of AutoCloseable objects */ 
public class ResourceCollection<T extends AutoCloseable> 
     implements Iterable<T>, AutoCloseable { 

    /** Resources owned by this instance */ 
    private final List<T> myResources; 

    /** 
    * Constructor 
    * @param allocator Function used to allocate each resource 
    * @param count  Number of times to call the allocator 
    * @throws E Thrown if any of the allocators throw 
    */ 
    public <E extends Throwable> ResourceCollection(
      final ThrowingSupplier<T, E> allocator, final int count) 
      throws E { 
     myResources = new ArrayList<>(count); 
     try { 
      while (myResources.size() < count) { 
       final T resource = allocator.getThrows(); 
       myResources.add(resource); 
      } 
     } catch (final Throwable e) { 
      close(); 
      throw e; 
     } 
    } 

    /** 
    * Constructor 
    * @param allocator Function used to allocate each resource 
    * @param input  List of input parameters passed to the allocator 
    * @throws E Thrown if any of the allocators throw 
    */ 
    public <U, E extends Throwable> ResourceCollection(
      final ThrowingFunction<U, T, E> allocator, final Collection<U> input) 
      throws E { 
     myResources = new ArrayList<>(input.size()); 
     try { 
      for (final U value : input) { 
       final T resource = allocator.applyThrows(value); 
       myResources.add(resource); 
      } 
     } catch (final Throwable e) { 
      close(); 
      throw e; 
     } 
    } 

    /** 
    * Gets the number of resources in the collection 
    * @return The number of resources in the collection 
    */ 
    public int size() { 
     return myResources.size(); 
    } 

    /** 
    * Gets whether the collection contains no resources 
    * @return Whether the collection contains no resources 
    */ 
    public boolean isEmpty() { 
     return myResources.isEmpty(); 
    } 

    /** 
    * Gets the resource at index i 
    * @param i The index of a resource, in the range [0, size()) 
    * @return The resource at index i 
    */ 
    public T get(final int i) { 
     return myResources.get(i); 
    } 

    @Override 
    public Iterator<T> iterator() { 
     return myResources.iterator(); 
    } 

    @Override 
    public void close() { 
     final ListIterator<T> resourceIter = 
       myResources.listIterator(myResources.size()); 
     while (resourceIter.hasPrevious()) { 
      final T resource = resourceIter.previous(); 
      if (resource != null) { 
       try { 
        resource .close(); 
        resourceIter.remove(); 
       } catch (final Throwable t) { 
        // Eat the exception 
       } 
      } 
     } 
    } 

} 
1

Was Sie tun, ist praktisch so gut wie es geht.

Sie könnten eine AutoCloseable Allzweck- Mehr näher schaffen, die eine List<AutoCloseable> und akzeptiert als Konstruktor-Parameter aufrufen, um eine Zählung von closeables und einer Fabrik enthält jede verschließbare zu erstellen, und schließen Sie sie dann alle, wenn seine close() aufgerufen wird, so dass Sie es wie folgt verwenden können:

try(MultiCloser<MulticastSocket> multiCloser = 
     new MultiCloser<>(ports.size(), i -> new MulticastSocket(ports.get(i))) 
{ 
    for(MulticastSocket socket : multiCloser.getItems()) 
    { 
     do something with the socket 
    } 
} 

... aber es wäre wahrscheinlich ein Overkill.

+0

Was 'port' in dem Lambda-Ausdruck ist? – saka1029

+0

@ saka1029 argh, dort, reparierte es. Aber darum geht es nicht. Der Punkt ist das Konstrukt. Die Details werden dem Schüler als Übung überlassen. –

2

Tun Sie es rekursiv die Garantien zu halten von Try-with-Ressourcen:

void foo(List<Integer> ports, List<Socket> sockets) { 
    if (sockets.size() == ports.size()) { 
    // Do something with your sockets. 
    } else { 
    try (Socket s = new MulticastSocket(ports.get(sockets.size())) { 
     sockets.add(s); 
     foo(ports, sockets); 
     // You could call sockets.remove(sockets.size()-1) here. 
     // Not convinced whether it's worth it. 
    } 
    } 
} 
+0

Sie können also keine Schleife, aber Sie können einen rekursiven Aufruf erstellen? das ist wirklich nett – Eugene

+0

Ich musste dies zweimal lesen, um zu verstehen, was Sie taten. +1 für Kreativität und Kürze, aber ich denke, ich bevorzuge immer noch Mike's Vorschlag, stattdessen eine "AutoCloseable" Klasse zu erstellen. Es scheint lesbarer zu sein, und die Rekursion zu missbrauchen, fühlt sich einfach ... schmutzig an. –

+0

@JeffG Sie sollten tun, was Sie sich wohl fühlen, denn schließlich müssen Sie es beibehalten; Aber ich denke, Sie werden es ziemlich unordentlich und schwierig finden, genau dieselbe Semantik von mehreren TWR-Blöcken ohne etwas Ähnliches zu implementieren. –

0

Was der Punkt ist ein ArrayList zum Speichern der MulticastSocket Instanzen zu benutzen?

sagte Sie, dass:

Ich werde dann die gleichen Daten zu jedem der N-Buchsen innerhalb einer Schleife senden und schließlich jede Buchse schließen.

So können Sie sie in einer Schleife erstellen und für jede Iteration die gleiche Verarbeitung senden.
Um es zu tun, sollten Sie ein wenig Ihr Design ändern.
Die Verarbeitungsaufgabe des MulticastSocket könnte von einer funktionalen Schnittstelle ausgeführt werden, die es auch ermöglicht, den zu verwendenden Port anzugeben.

Zum Beispiel:

@FunctionalInterface 
public interface SocketProcessor { 
    void process(MulticastSocket multicastSocket) ; 
} 

Sie eine Methode haben könnte, das diese funktionale Schnittstelle als Parameter übernimmt die Verarbeitung anzuwenden:

public static void processSocket(SocketProcessor socketProcessor, Integer port) throws IOException { 
    try (final MulticastSocket socket = new MulticastSocket(port)) { 
    socketProcessor.process(socket); 
    } 
} 

Endlich vom Client-Code, können Sie eine erstellen socketProzessorinstanz mit einem Lambda:

SocketProcessor socketProcessor = (MulticastSocket socket) -> { 
    socket.send(...); 
    socket.send(...); 
}; 

Und dann könnte man Schleife an den Ports, um processSocket mit dem geeigneten Hafen und die SocketProcessor Instanz gerade erstellt aufzurufen:

for (final Integer port : ports) { 
    try { 
     processSocket(socketProcessor, port); 
    } catch (IOException e) { 
     // do processing 
    } 
} 

Diese Lösung nicht erforderlich ist, kürzer (ohne wirklich mehr zu sein), aber es ist wirklich klarer.
Die beiden sind Hauptanliegen getrennt:

  • processSocket(SocketProcessor), die die Kesselblech Code

  • SocketProcessor, die die konkrete Aufgabe definiert, durchführt.

+0

Ihre Antwort machte mir klar, dass ich vergessen habe anzugeben, dass die Pakete verschachtelt gesendet werden müssen (z. B. Paket 1 an alle Sockets senden, dann Paket 2 an alle Sockets senden, ...). Da dies eine meiner Anforderungen ist (die in meiner Frage zugegebenermaßen nicht vorhanden ist), würde diese Antwort das Erstellen eines neuen Sockets für jedes Paket erfordern, das gesendet werden muss. –

+0

@Jeff G In diesem Fall entspricht die vorgeschlagene Antwort in der Tat nicht Ihren Bedürfnissen. – davidxxx