本文的目的是介绍接口是Java8中实用程序类的有效替代品吗?[重复]的详细情况,特别关注java中使用接口的好处的相关信息。我们将通过专业的研究、有关数据的分析等多种方式,为您呈现一个全面的了解接口是
本文的目的是介绍接口是Java 8中实用程序类的有效替代品吗?[重复]的详细情况,特别关注java中使用接口的好处的相关信息。我们将通过专业的研究、有关数据的分析等多种方式,为您呈现一个全面的了解接口是Java 8中实用程序类的有效替代品吗?[重复]的机会,同时也不会遗漏关于haskell – Data.Vector.dropWhile的有效替代品、java – 使用枚举实现实用程序类和单例、java – 强制实用程序类应该是final和private构造函数吗?、JavaFX GUI Updater实用程序类中的并发问题的知识。
本文目录一览:- 接口是Java 8中实用程序类的有效替代品吗?[重复](java中使用接口的好处)
- haskell – Data.Vector.dropWhile的有效替代品
- java – 使用枚举实现实用程序类和单例
- java – 强制实用程序类应该是final和private构造函数吗?
- JavaFX GUI Updater实用程序类中的并发问题
接口是Java 8中实用程序类的有效替代品吗?[重复](java中使用接口的好处)
在过去十年左右的时间里,我一直在将以下模式用于Java实用程序类。该类仅包含静态方法和字段,已声明final
为无法扩展,并且具有private
构造函数因此无法实例化。
public final class SomeUtilityClass { public static final String SOME_CONSTANT = "Some constant"; private SomeUtilityClass() {} public static Object someUtilityMethod(Object someParameter) { /* ... */ return null; }}
现在,随着Java 8
接口中静态方法的引入,我最近发现自己使用了实用程序接口模式:
public interface SomeUtilityInterface { String SOME_CONSTANT = "Some constant"; static Object someUtilityMethod(Object someParameter) { /* ... */ return null; }}
这使我摆脱构造,和大量的关键字(个public
,static
,final
),这些接口中隐含的。
这种方法有不利之处吗?通过实用程序接口使用实用程序类有什么好处?
答案1
小编典典以为Constant Interface模式创建反模式的人为基础,我想说,尽管您不打算让客户端实现该接口,但它仍然有可能,可能更容易并且 不应被允许
:
API应该易于使用且难以滥用。做简单的事情应该很容易;可能做复杂的事情; 并且不可能或至少很难做错事。
尽管如下所述,但这确实取决于目标受众
许多易于使用的设计模式都受到了很多批评(上下文模式,单例模式,常量接口模式)。哎呀,甚至像得墨meter耳定律这样的设计原则也因过于冗长而受到批评。
我不想这么说,但是这些决定都是基于意见的。尽管上下文模式被视为一种反模式, 但在诸如Spring和Android SDK之类的主流框架中很明显
。它归结为环境以及目标受众。
我可以找到的主要缺点 是在Constant Interface
wiki中的“缺点”下列出的第三项内容:
如果将来的发行版中要求二进制代码兼容,则常量接口必须永远保持一个接口(无法将其转换为类), 即使它在常规意义上并未用作接口。
如果您曾经想过“嘿,这实际上不是合同,我想执行更强大的设计”,则您将无法对其进行更改。但正如我所说,这取决于您;也许您将来不会在意更改它。
最重要的是,@
TagirValeev提到的代码清晰度。接口的目的是实现;如果您不希望某人实现您提供的API,请不要使其实现。但是,我认为这与“目标受众”声明有关。不会撒谎,我会在不太冗长的基础上与您同在,但这取决于我的代码适用于谁。不想使用可能会被检查的代码的常量接口。
haskell – Data.Vector.dropWhile的有效替代品
module Main where import Criterion.Main import qualified Data.Vector as V f1 :: V.Vector Double -> Double f1 xs | V.null xs = 0 | otherwise = V.last xss / V.head xss where xss = V.dropWhile (< 10) xs f2 :: V.Vector Double -> Double f2 xs | V.null xs = 0 | otherwise = V.last xs / V.head xs setupEnv :: IO (V.Vector Double) setupEnv = return $V.enumFromN 0 10000000 main :: IO () main = defaultMain [ env setupEnv $\v -> bgroup "funcs" [bench "f1" $nf f1 v,bench "f2" $nf f2 v] ]
使用–make -O2进行编译并运行会得到以下结果:
app $./A benchmarking funcs/f1 time 81.87 ms (78.34 ms .. 86.06 ms) 0.998 R² (0.996 R² .. 1.000 R²) mean 85.87 ms (84.16 ms .. 87.13 ms) std dev 2.351 ms (1.169 ms .. 3.115 ms) benchmarking funcs/f2 time 27.50 ns (27.11 ns .. 27.95 ns) 0.998 R² (0.996 R² .. 0.999 R²) mean 27.62 ns (27.21 ns .. 28.05 ns) std dev 1.391 ns (1.154 ns .. 1.744 ns) variance introduced by outliers: 73% (severely inflated)
简单地取第一个和最后一个元素并将它们分开的平均执行时间是~27ns.丢弃前9个元素并执行相同操作的平均值约为85毫秒或慢3000倍.
使用未装箱的向量可将f1的性能提高一半以上,但我需要支持没有“UnBoxed”类实例的元素.
根据dropWhile documentation,它具有O(n)的复杂性,但它没有复制. Haskell库中是否有数据结构支持高效的dropWhile类型操作以及对第一个和最后一个元素的O(1)访问?
解决方法
作为权宜之计,您可以实现自定义dropWhile.我使用以下代码的基准测试:
dropWhile' :: (a -> Bool) -> V.Vector a -> V.Vector a dropWhile' p v = V.drop (go 0) v where go n | n == V.length v = n | p (V.unsafeIndex v n) = go (n + 1) | otherwise = n
并得到以下结果:
benchmarking funcs/f1 time 57.70 ns (56.35 ns .. 59.46 ns) benchmarking funcs/f2 time 19.68 ns (19.44 ns .. 19.91 ns)
java – 使用枚举实现实用程序类和单例
首先,我没有忘记 – 我甚至没有意识到:)
这些方法简洁明了 – 与更传统的实现相同方法的方法相比,除了简洁之外还有什么好处,比如使用带有私有构造函数的final类来实现实用程序类?
此外,是否有任何问题(除了令人困惑的程序员不期待它)?
解决方法
在实用程序类中使用枚举用于单例是很好的,但我通常会尝试保持枚举被用作内部实现细节的事实.例如,参见Guava的Predicates类,它使用枚举来强制执行某些谓词的单个实例,例如alwaysTrue().但它不会向用户公开枚举.
至于其他好处:是的,还有其他好处,例如内置的可串行化,并且每个类加载器绝对强制执行枚举常量的单个实例,即使在反序列化时也是如此.
java – 强制实用程序类应该是final和private构造函数吗?
解决方法
为什么遵循这一惯例,已在其他答案中解释,甚至OP也涵盖了这一点.
我想进一步解释一下,大多数Utility类都有与对象实例无关的方法/函数.这些是聚合函数.因为它们仅依赖于返回值的参数而不依赖于实用程序类的类变量.因此,大多数这些函数/方法都是静态的.因此,Utility类理想地是具有所有静态方法的类.
因此,任何调用这些方法的程序员都不需要实例化这个类.然而,一些机器人编码器(可能没有经验或兴趣)将倾向于在调用其方法之前创建他们认为需要的对象.为避免创建对象,我们有3个选项: –
>继续教育人们不要实例化它. (没有理智的人可以继续这样做.)
>将类标记为抽象: –
现在robo-coders再次不会创建对象.然而,审查和更广泛的Java社区将争辩说,标记抽象意味着你希望有人扩展它.所以,这也不是一个好选择.
>私人建设者: –
受保护将再次允许子类创建对象.
现在,再次如果有人想为这些实用程序类添加一些功能的新方法,他不需要扩展它,他可以添加新方法,因为每个方法都是独立的,没有机会破坏其他功能.所以,不需要覆盖它.而且你也不会去见,所以需要将它子类化.最好将其标记为最终.
总之,创建实用程序类的对象没有意义.因此构造函数应该是私有的.而你永远不想覆盖它,所以最后标记它.
JavaFX GUI Updater实用程序类中的并发问题
我正在JavaFX中为大型Java项目构建GUI。这个项目有许多不同的工作线程在后台执行一些繁重的计算,我试图在GUI中可视化这些工作线程的进度。所谓进步,是指不仅是裸露的百分比,而且是Task类中未包含的其他变量,例如:
- 当前文件
- 当前错误计数
- 到目前为止读取的字节数
- …
由于这些进度变量的变化非常快,并且由于必须从JavaFX线程(Platform.runLater())执行GUI更新,因此JavaFX事件队列很快就会过载。我正在尝试通过构建实用程序类来解决此问题,该实用程序类能够从JavaFX线程外部异步更新GUI属性。应该跳过快速连续的更新,以便仅显示最新值,从而避免用Runnables挤满JavaFX事件队列。
因此,我构建了以下类GUIUpdater
以将Properties(通常是GUI元素,如Label)绑定到ObservableValues(例如SimpleStringProperty)。此类具有两个InnerClasses:
PropertyUpdater
负责将单个Property绑定到单个ObservableValue并进行更新。- 在
Updater
提供了用于Platform.runLater可重复使用的Runnable对象()。
实用程序类:
package main;import java.util.concurrent.ConcurrentLinkedQueue;import javafx.application.Platform;import javafx.beans.property.Property;import javafx.beans.value.ChangeListener;import javafx.beans.value.ObservableValue;/** * Class for enabling fast updates of GUI components from outside the JavaFX thread. * Updating GUI components (such as labels) should be done from the JavaFX thread by using Platform.runLater for example. * This makes it hard to update the GUI with a fast changing variable as it is very easy to fill up the JavaFX event queue faster than it can be emptied (i.e. faster than it can be drawn). * This class binds ObservableValues to (GUI) Properties and ensures that quick consecutive updates are ignored, only updating to the latest value. */public class GUIUpdater { private ConcurrentLinkedQueue<PropertyUpdater<?>> dirtyPropertyUpdaters = new ConcurrentLinkedQueue<>(); private Updater updater = new Updater(); private boolean isUpdating = false; /** * Binds an ObservableValue to a Property. * Updates to the ObservableValue can be made from outside the JavaFX thread and the latest update will be reflected in the Property. * @param property (GUI) Property to be updated/ * @param observable ObservableValue to update the GUI property to. */ public <T> void bind(Property<T> property, ObservableValue<T> observable) { PropertyUpdater<T> propertyUpdater = new PropertyUpdater<>(property, observable); observable.addListener(propertyUpdater); } /** * Unbinds the given ObservableValue from the given Property. * Updates to the ObservableValue will no longer be reflected in the Property. * @param property (GUI) Property to unbind the ObservableValue from. * @param observable ObservableValue to unbind from the given Property. */ public <T> void unbind(Property<T> property, ObservableValue<T> observable) { PropertyUpdater<T> tmpPropertyUpdater = new PropertyUpdater<>(property, observable); observable.removeListener(tmpPropertyUpdater); } /** * Schedules an update to the GUI by using a call to Platform.runLater(). * The updated property is added to the dirtyProperties list, marking it for the next update round. * Will only submit the event to the event queue if the event isn''t in the event queue yet. * @param updater */ private void scheduleUpdate(PropertyUpdater<?> updater) { this.dirtyPropertyUpdaters.add(updater); // Make sure the isUpdating var isn''t changed concurrently by the Updater thread (on the JavaFX event queue) synchronized (this) { if (!this.isUpdating) { this.isUpdating = true; Platform.runLater(this.updater); } } } /** * Class used for binding a single ObservableValue to a Property and updating it. * * @param <T> */ private class PropertyUpdater<T> implements ChangeListener<T> { private boolean isDirty = false; private Property<T> property = null; private ObservableValue<T> observable = null; public PropertyUpdater(Property<T> property, ObservableValue<T> observable) { this.property = property; this.observable = observable; } @Override /** * Called whenever the ObservableValue has changed. Marks this Updater as dirty. */ public synchronized void changed(ObservableValue<? extends T> observable, T oldValue, T newValue) { if (!this.isDirty) { this.isDirty = true; GUIUpdater.this.scheduleUpdate(this); } } /** * Updates the Property to the ObservableValue and marks it as clean again. * Should only be called from the JavaFX thread. */ public synchronized void update() { T value = this.observable.getValue(); this.property.setValue(value); this.isDirty = false; } @Override /** * Two PropertyUpdaters are equals if their Property and ObservableValue map to the same object (address). */ public boolean equals(Object otherObj) { PropertyUpdater<?> otherUpdater = (PropertyUpdater<?>) otherObj; if (otherObj == null) { return false; } else { // Only compare addresses (comparing with equals also compares contents): return (this.property == otherUpdater.property) && (this.observable == otherUpdater.observable); } } } /** * Simple class containing the Runnable for the call to Platform.runLater. * Hence, the run() method should only be called from the JavaFX thread. * */ private class Updater implements Runnable { @Override public void run() { // Loop through the individual PropertyUpdaters, updating them one by one: while(!GUIUpdater.this.dirtyPropertyUpdaters.isEmpty()) { PropertyUpdater<?> curUpdater = GUIUpdater.this.dirtyPropertyUpdaters.poll(); curUpdater.update(); } // Make sure we''re not clearing the mark when scheduleUpdate() is still setting it: synchronized (GUIUpdater.this) { GUIUpdater.this.isUpdating = false; } } }}
这是测试GUIUpdater
实用程序类的简单类:
package main;import javafx.application.Application;import javafx.beans.property.SimpleStringProperty;import javafx.concurrent.Task;import javafx.event.ActionEvent;import javafx.event.EventHandler;import javafx.scene.Scene;import javafx.scene.control.Button;import javafx.scene.control.Label;import javafx.scene.control.ProgressBar;import javafx.scene.layout.FlowPane;import javafx.stage.Stage;public class JavaFXTest extends Application { private GUIUpdater guiUpdater = new GUIUpdater(); private Label lblState = new Label(); private ProgressBar prgProgress = new ProgressBar(); public static void main(String args[]) { JavaFXTest.launch(args); } @Override public void start(Stage primaryStage) throws Exception { // Init window: FlowPane flowPane = new FlowPane(); primaryStage.setScene(new Scene(flowPane)); primaryStage.setTitle("JavaFXTest"); // Add a Label and a progressBar: flowPane.getChildren().add(this.lblState); flowPane.getChildren().add(this.prgProgress); // Add button: Button btnStart = new Button("Start"); btnStart.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { // Create task: TestTask testTask = new TestTask(); // Bind: JavaFXTest.this.guiUpdater.bind(JavaFXTest.this.lblState.textProperty(), testTask.myStateProperty()); JavaFXTest.this.prgProgress.progressProperty().bind(testTask.progressProperty()); // No need to use GUIUpdater here, Task class provides the same functionality for progress. // Start task: Thread tmpThread = new Thread(testTask); tmpThread.start(); } }); flowPane.getChildren().add(btnStart); // Show: primaryStage.show(); } /** * A simple task containing a for loop to simulate a fast running and fast updating process. * @author DePhille * */ private class TestTask extends Task<Void> { private SimpleStringProperty myState = new SimpleStringProperty(); @Override protected Void call() throws Exception { // Count: try { int maxValue = 1000000; System.out.println("Starting..."); for(int i = 0; i < maxValue; i++) { this.updateProgress(i, maxValue - 1); this.myState.set("Hello " + i); } System.out.println("Done!"); } catch(Exception e) { e.printStackTrace(); } // Unbind: JavaFXTest.this.guiUpdater.unbind(JavaFXTest.this.lblState.textProperty(), this.myStateProperty()); return null; } public SimpleStringProperty myStateProperty() { return this.myState; } }}
代码的问题是,有时Label不会更新为最新值(在这种情况下为999999)。似乎大多数情况是在应用程序启动后立即发生的,因此,启动应用程序,单击“开始”按钮,关闭它,然后重复此过程,应在尝试几次后重现该问题。据我所知,我synchronized
在需要的地方添加了块,这就是为什么我不明白问题出在哪里的原因。
即使我主要是在寻找所描述问题的解决方案,也非常感谢所有建议(甚至那些与问题无关的建议)!我也在代码中添加了注释,因此,我希望与上面的信息一起提供有关问题和代码的足够详细信息。
提前致谢!
答案1
小编典典我自己可以解决此问题。System.out
在各个地方添加几天后,结果发现问题出在isUpdating
变量的并发问题上。当JavaFX线程位于while
循环和中的synchronized
块之间时,发生了问题Updater.run
。我通过使Updater.run
和GUIUpdater.scheduleUpdate
方法在同一对象上同步来解决了这个问题。
我也将其GUIUpdater
制成了仅静态对象,因为Runnables
无论其他GUIUpdater
实例如何,都有多个实例将放置在JavaFX事件队列中,从而阻塞了事件队列。总而言之,这是结果GUIUpdater
类:
package be.pbeckers.javafxguiupdater;import java.util.concurrent.ConcurrentLinkedQueue;import javafx.application.Platform;import javafx.beans.property.Property;import javafx.beans.value.ChangeListener;import javafx.beans.value.ObservableValue;/** * Class for enabling fast updates of GUI components from outside the JavaFX thread. * Updating GUI components (such as labels) should be done from the JavaFX thread by using Platform.runLater for example. * This makes it hard to update the GUI with a fast changing variable as it is very easy to fill up the JavaFX event queue faster than it can be emptied (i.e. faster than it can be drawn). * This class binds ObservableValues to (GUI) Properties and ensures that quick consecutive updates are ignored, only updating to the latest value. */public abstract class GUIUpdater { private static ConcurrentLinkedQueue<PropertyUpdater<?>> dirtyPropertyUpdaters = new ConcurrentLinkedQueue<>(); private static Updater updater = new Updater(); private static boolean isUpdating = false; /** * Binds an ObservableValue to a Property. * Updates to the ObservableValue can be made from outside the JavaFX thread and the latest update will be reflected in the Property. * @param property (GUI) Property to be updated/ * @param observable ObservableValue to update the GUI property to. */ public static <T> void bind(Property<T> property, ObservableValue<T> observable) { PropertyUpdater<T> propertyUpdater = new PropertyUpdater<>(property, observable); observable.addListener(propertyUpdater); } /** * Unbinds the given ObservableValue from the given Property. * Updates to the ObservableValue will no longer be reflected in the Property. * @param property (GUI) Property to unbind the ObservableValue from. * @param observable ObservableValue to unbind from the given Property. */ public static <T> void unbind(Property<T> property, ObservableValue<T> observable) { PropertyUpdater<T> tmpPropertyUpdater = new PropertyUpdater<>(property, observable); observable.removeListener(tmpPropertyUpdater); } /** * Schedules an update to the GUI by using a call to Platform.runLater(). * The updated property is added to the dirtyProperties list, marking it for the next update round. * Will only submit the event to the event queue if the event isn''t in the event queue yet. * @param updater */ private static synchronized void scheduleUpdate(PropertyUpdater<?> updater) { GUIUpdater.dirtyPropertyUpdaters.add(updater); if (!GUIUpdater.isUpdating) { GUIUpdater.isUpdating = true; Platform.runLater(GUIUpdater.updater); } } /** * Class used for binding a single ObservableValue to a Property and updating it. * * @param <T> */ private static class PropertyUpdater<T> implements ChangeListener<T> { private boolean isDirty = false; private Property<T> property = null; private ObservableValue<T> observable = null; public PropertyUpdater(Property<T> property, ObservableValue<T> observable) { this.property = property; this.observable = observable; } @Override /** * Called whenever the ObservableValue has changed. Marks this Updater as dirty. */ public synchronized void changed(ObservableValue<? extends T> observable, T oldValue, T newValue) { if (!this.isDirty) { this.isDirty = true; GUIUpdater.scheduleUpdate(this); } } /** * Updates the Property to the ObservableValue and marks it as clean again. * Should only be called from the JavaFX thread. */ public synchronized void update() { T value = this.observable.getValue(); this.property.setValue(value); this.isDirty = false; } @Override /** * Two PropertyUpdaters are equals if their Property and ObservableValue map to the same object (address). */ public boolean equals(Object otherObj) { PropertyUpdater<?> otherUpdater = (PropertyUpdater<?>) otherObj; if (otherObj == null) { return false; } else { // Only compare addresses (comparing with equals also compares contents): return (this.property == otherUpdater.property) && (this.observable == otherUpdater.observable); } } } /** * Simple class containing the Runnable for the call to Platform.runLater. * Hence, the run() method should only be called from the JavaFX thread. * */ private static class Updater implements Runnable { @Override public void run() { synchronized (GUIUpdater.class) { // Loop through the individual PropertyUpdaters, updating them one by one: while(!GUIUpdater.dirtyPropertyUpdaters.isEmpty()) { PropertyUpdater<?> curUpdater = GUIUpdater.dirtyPropertyUpdaters.poll(); curUpdater.update(); } // Mark as updated: GUIUpdater.isUpdating = false; } } }}
这是测试器类的稍有更新的版本(由于它完全不重要,因此我不对其进行详细介绍):
package be.pbeckers.javafxguiupdater.test;import be.pbeckers.javafxguiupdater.GUIUpdater;import javafx.application.Application;import javafx.beans.property.SimpleStringProperty;import javafx.concurrent.Task;import javafx.event.ActionEvent;import javafx.event.EventHandler;import javafx.scene.Scene;import javafx.scene.control.Button;import javafx.scene.control.Label;import javafx.scene.control.ProgressBar;import javafx.scene.layout.FlowPane;import javafx.stage.Stage;public class JavaFXTest extends Application { private Label lblCurFile = new Label(); private Label lblErrors = new Label(); private Label lblBytesParsed = new Label(); private ProgressBar prgProgress = new ProgressBar(); public static void main(String args[]) { JavaFXTest.launch(args); } @Override public void start(Stage primaryStage) throws Exception { // Init window: FlowPane flowPane = new FlowPane(); primaryStage.setScene(new Scene(flowPane)); primaryStage.setTitle("JavaFXTest"); // Add a few Labels and a progressBar: flowPane.getChildren().add(this.lblCurFile); flowPane.getChildren().add(this.lblErrors); flowPane.getChildren().add(this.lblBytesParsed); flowPane.getChildren().add(this.prgProgress); // Add button: Button btnStart = new Button("Start"); btnStart.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { // Create task: TestTask testTask = new TestTask(); // Bind: GUIUpdater.bind(JavaFXTest.this.lblCurFile.textProperty(), testTask.curFileProperty()); GUIUpdater.bind(JavaFXTest.this.lblErrors.textProperty(), testTask.errorsProperty()); GUIUpdater.bind(JavaFXTest.this.lblBytesParsed.textProperty(), testTask.bytesParsedProperty()); JavaFXTest.this.prgProgress.progressProperty().bind(testTask.progressProperty()); // No need to use GUIUpdater here, Task class provides the same functionality for progress. // Start task: Thread tmpThread = new Thread(testTask); tmpThread.start(); } }); flowPane.getChildren().add(btnStart); // Show: primaryStage.show(); } /** * A simple task containing a for loop to simulate a fast running and fast updating process. * @author DePhille * */ private class TestTask extends Task<Void> { private SimpleStringProperty curFile = new SimpleStringProperty(); private SimpleStringProperty errors = new SimpleStringProperty(); private SimpleStringProperty bytesParsed = new SimpleStringProperty(); @Override protected Void call() throws Exception { // Count: try { int maxValue = 1000000; long startTime = System.currentTimeMillis(); System.out.println("Starting..."); for(int i = 0; i < maxValue; i++) { this.updateProgress(i, maxValue - 1); // Simulate some progress variables: this.curFile.set("File_" + i + ".txt"); if ((i % 1000) == 0) { //this.errors.set("" + (i / 1000) + " Errors"); } //this.bytesParsed.set("" + (i / 1024) + " KBytes"); } long stopTime = System.currentTimeMillis(); System.out.println("Done in " + (stopTime - startTime) + " msec!"); } catch(Exception e) { e.printStackTrace(); } // Unbind: GUIUpdater.unbind(JavaFXTest.this.lblCurFile.textProperty(), this.curFileProperty()); GUIUpdater.unbind(JavaFXTest.this.lblErrors.textProperty(), this.errorsProperty()); GUIUpdater.unbind(JavaFXTest.this.lblBytesParsed.textProperty(), this.bytesParsedProperty()); return null; } public SimpleStringProperty curFileProperty() { return this.curFile; } public SimpleStringProperty errorsProperty() { return this.errors; } public SimpleStringProperty bytesParsedProperty() { return this.bytesParsed; } }}
我们今天的关于接口是Java 8中实用程序类的有效替代品吗?[重复]和java中使用接口的好处的分享已经告一段落,感谢您的关注,如果您想了解更多关于haskell – Data.Vector.dropWhile的有效替代品、java – 使用枚举实现实用程序类和单例、java – 强制实用程序类应该是final和private构造函数吗?、JavaFX GUI Updater实用程序类中的并发问题的相关信息,请在本站查询。
本文标签: