【问题标题】:Java Try-With-Resources Unknown Resource CountJava Try-With-Resources 未知资源计数
【发布时间】:2017-11-18 09:50:42
【问题描述】:

我需要打开 N 个多播套接字(其中 N 来自参数列表的大小)。然后,我将在一个循环中将相同的数据发送到 N 个套接字中的每一个,最后关闭每个套接字。我的问题是,如何使用 try-with-resources 块执行此操作?以下是我将如何使用单个资源执行此操作:

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
    ...
}

我能想到的使用多个端口的唯一方法是:

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
        }
    }
}

是否有更简洁的方法来实现这一点,或者我提出的解决方案是否尽可能好?

【问题讨论】:

    标签: java try-with-resources autocloseable


    【解决方案1】:

    递归执行以保持 try-with-resources 的保证:

    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.
        }
      }
    }
    

    【讨论】:

    • 所以你不能循环但你可以创建一个递归调用?这真的很好
    • 我不得不读了两遍才能理解你在做什么。 +1 创意和简洁,但我认为我仍然更喜欢 Mike 关于创建实用程序 AutoCloseable 类的建议。它似乎更具可读性,并且为此滥用递归感觉......很脏。
    • @JeffG 你应该做你觉得舒服的事,因为最终你必须维护它;但我认为如果没有这样的东西,你会发现实现多个 TWR 块的完全等效语义非常混乱和困难。
    【解决方案2】:

    你所做的几乎和它得到的一样好。

    您可以创建一个 AutoCloseable 通用多用途关闭器,其中包含一个 List&lt;AutoCloseable&gt; 并接受作为构造函数参数的可关闭对象的计数和调用以创建每个可关闭对象的工厂,然后在其 @ 时将它们全部关闭987654323@ 被调用,所以你可以这样使用它:

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

    ...但这可能有点矫枉过正。

    【讨论】:

    • lambda 表达式中的port 是什么?
    • @saka1029 啊,在那里,修好了。但这不是重点。重点是构造。细节留给学生作为练习。
    【解决方案3】:

    使用ArrayList 存储MulticastSocket 实例有什么意义?

    你说过:

    然后我会将相同的数据发送到一个内的 N 个套接字中的每一个 循环,最后关闭每个套接字。

    因此您可以在循环中创建它们并为每次迭代发送相同的处理。
    要做到这一点,您应该稍微改变一下您的设计。
    MulticastSocket 的处理任务可以由一个功能接口执行,该接口还允许指定要使用的端口。

    例如:

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

    你可以有一个方法,将这个函数接口作为参数来应用处理:

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

    最后,您可以从客户端代码中创建一个带有 lambda 的 socketProcessor 实例:

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

    然后您可以在端口上循环,以便使用合适的端口和刚刚创建的 SocketProcessor 实例调用 processSocket:

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

    这个解决方案不需要更短(实际上不会更长),但它确实更清晰。
    两个主要问题是分开的:

    • processSocket(SocketProcessor) 执行样板代码

    • SocketProcessor 定义具体任务。

    【讨论】:

    • 您的回答让我意识到我忘记指定需要交错发送数据包(例如,在所有套接字上发送数据包 1,然后在所有套接字上发送数据包 2,...)。鉴于这是我的要求之一(在我的问题中肯定没有),这个答案需要为每个需要发送的数据包创建一个新的套接字。
    • @Jeff G 在这种情况下,确实建议的答案不适合您的需要。
    【解决方案4】:

    受 Mike Nakis 提出的想法的启发,我想出了以下课程……

    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
                    }
                }
            }
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 2013-09-12
      • 2020-01-01
      • 1970-01-01
      • 2016-09-29
      • 2012-03-04
      • 2011-10-16
      • 1970-01-01
      • 2020-10-23
      • 2013-07-21
      相关资源
      最近更新 更多