我在Java中有两个相似但不同类型的代码块:
private Integer readInteger() {
Integer value = null;
while (value == null) {
if (scanner.hasNextInt()) {
value = scanner.nextInt();
} else {
scanner.next();
}
}
return value;
}
private Double readDouble() {
Double value = null;
while (value == null) {
if (scanner.hasNextDouble()) {
value = scanner.nextDouble();
} else {
scanner.next();
}
}
return value;
}
是否可以只制作一种适用于它们的方法?
答案 0 :(得分:38)
我会说,使用泛型方法,结合Java 8中引入的functional interfaces。
方法read
现在变为higher order function。
private <T> T read(Predicate<Scanner> hasVal, Function<Scanner, T> nextVal) {
T value = null;
while (value == null) {
if (hasVal.test(scanner)) {
value = nextVal.apply(scanner);
} else {
scanner.next();
}
}
return value;
}
调用代码变为:
read(Scanner::hasNextInt, Scanner::nextInt);
read(Scanner::hasNextDouble, Scanner::nextDouble);
read(Scanner::hasNextFloat, Scanner::nextFloat);
// ...
因此readInteger()
方法可以按如下方式进行调整:
private Integer readInteger() {
return read(Scanner::hasNextInt, Scanner::nextInt);
}
答案 1 :(得分:5)
您可以使用三种方法:
例如:
private void scheduleservicelist_itemappearing(object sender, xamarin.forms.itemvisibilityeventargs e)
{
var currentitem = e.item as mymodel;
if (viewmodel.mylistlist[0].id == currentitem.id)
{
headerlayout.isvisible = true; headinggrid.margin = new thickness(0,-70, 0, 0);
}
}
private void scheduleservicelist_itemdisappearing(object sender, itemvisibilityeventargs e)
{
var currentitem = e.item as mymodel;
if (viewmodel.mylistlist[0].id == currentitem.id)
{
headerlayout.isvisible = false; headinggrid.margin = new thickness(0, 0, 0, 0);
}
}
private void ScheduleServiceListView_Scrolled(object sender, ScrolledEventArgs e)
{
double scrollposition = e.ScrollY; if (scrollposition > previousScrollposition)
{
isScrollUp = true; isScrollDown = false;
}
else if(scrollposition < previousScrollposition)
{
isScrollDown => true; isScrollUp = false;
}
else
{
if (isScrollUp)
{
isScrollUp =true; isScrollDown = false;
}
else
{
isScrollDown = true; isScrollUp = false;
}
scrollposition = 0;
}
if (isScrollUp )
{
HeaderLayout.IsVisible = false;
HeadingGrid.Margin = new Thickness(0, 0, 0, 0);
HeadingGrid.VerticalOptions = LayoutOptions.Start;
}
else
{
HeaderLayout.IsVisible = true;
HeadingGrid.Margin = new Thickness(0, -70, 0, 0);
}
previousScrollposition = scrollposition;
}
您可以将其传递给您的方法:
interface Frobnitz<T> {
boolean has();
T get();
void discard();
}
然后只为您的private <T> T read(Frobnitz<? extends T> frob) {
T value = null;
while (value == null) {
if (frob.has()) {
value = frob.get();
} else {
frob.discard();
}
}
return value;
}
和Frobnitz
案例实施Double
。
说实话,我不确定这对你有多大帮助,特别是如果你只有两个案件;我倾向于只是为了吸收少量的重复。
答案 2 :(得分:5)
很多人已回答您可以使用泛型,但您也可以简单地删除readInteger
方法,并且只使用readDouble
,因为整数可以转换为双精度而不会丢失数据。
答案 3 :(得分:3)
这是关于代码重复。
一般方法是将类似代码(您有)转换为相等代码,可以将其提取到常见的参数化方法。< / p>
在你的情况下,使两个代码剪断的不同之处在于访问Scanner
的方法。你必须以某种方式封装它们。我建议用Java8 功能接口这样做:
@FunctionalInterface
interface ScannerNext{
boolean hasNext(Scanner scanner);
}
@FunctionalInterface
interface ScannerValue{
Number getNext(Scanner scanner);
}
然后用功能界面替换扫描仪中方法的实际调用:
private Integer readInteger() {
ScannerNext scannerNext = (sc)->sc.hasNextInt();
ScannerValue scannerValue = (sc)-> sc.nextInt();
Integer value = null;
while (value == null) {
if (scannerNext.hasNext(scanner)) {
value = scannerValue.getNext(scanner);
} else {
scanner.next();
}
}
return value;
}
还有一个问题是值变量的类型不同。所以我们用它的常用超类型替换它:
private Integer readInteger() {
ScannerNext scannerNext = (sc)->sc.hasNextInt();
ScannerValue scannerValue = (sc)-> sc.nextInt();
Number value = null;
while (value == null) {
if (scannerNext.hasNext(scanner)) {
value = scannerValue.getNext(scanner);
} else {
scanner.next();
}
}
return (Integer)value;
}
现在你需要一个大相等部分的地方。您可以选择以Number value = null;
开头的那些部分之一 }
之前的return ...
,并调用您的IDE 自动重构 提取方法:
private Number readNumber(ScannerNext scannerNext, ScannerValue scannerValue) {
Number value = null;
while (value == null) {
if (scannerNext.hasNext(scanner)) {
value = scannerValue.getNext(scanner);
} else {
scanner.next();
}
}
return value;
}
private Integer readInteger() {
return (Integer) readNumber( (sc)->sc.hasNextInt(), (sc)-> sc.nextInt());
}
private Double readDouble() {
return (Double) readNumber( (sc)->sc.hasNextDouble(), (sc)-> sc.nextDouble());
}
评论反对使用自定义接口来对抗JVM中的预定义接口。
但我在这个答案中的观点是如何将类似代码转换为相等代码,以便可以将其提取到单个方法而不是为此提供具体解决方案随机问题。
答案 4 :(得分:2)
与我的其他答案(以及其他答案)完全不同的方法:不要使用泛型,而只是简单地编写方法,所以你没有真正注意到重复。
TL; DR:将方法重写为
while (!scanner.hasNextX()) scanner.next();
return scanner.nextX();
总体目标 - 将其作为单一方法编写 - 只有在您接受一些额外的瑕疵时才有可能。
Java方法签名没有考虑返回类型,因此不可能让next()
方法在一个上下文中返回Integer
,而在另一个上下文中返回Double
返回一个常见的超类型。)
因此,你必须在呼叫站点有一些东西来区分这些情况:
您可以考虑传递Integer.class
或Double.class
之类的内容。这样做的好处是可以使用泛型来知道返回的值与该类型匹配。但是呼叫者可以传递其他内容:您将如何处理Long.class
或String.class
?您需要处理所有事情,或者您在运行时失败(不是一个好的选择)。即使绑定更严格(例如Class<? extends Number>
),您仍然需要处理的时间超过Integer
和Double
。
(更不用说在任何地方写Integer.class
和Double.class
真的很冗长了)
您可能会考虑做类似@Ward's answer的事情(我喜欢,BTW:如果你打算用泛型来做,那就这样做),并传递能够实现的功能对象处理感兴趣的类型,以及提供类型信息以指示返回类型。
但是,再一次,你必须在每个呼叫站点传递这些功能对象,这真的很冗长。
在采用这些方法之一时,您可以添加辅助方法,将适当的参数传递给“通用”read
方法。但这感觉就像是一个向后的步骤:不是将方法的数量减少到1,而是将增加为3。
此外,您现在必须在呼叫站点以某种方式区分这些辅助方法,以便能够调用适当的方法:
您可以使用 value 类型的参数进行重载,而不是类类型,例如
Double read(Double d)
Integer read(Integer d)
然后调用Double d = read(0.0); Integer i = read(0);
。但任何阅读此代码的人都会想知道代码中的神奇数字是什么 - 0
是否有任何意义?
或者,更简单,只需调用两个不同的重载:
Double readDouble()
Integer readInteger()
这很简单:虽然它比read(0.0)
稍微冗长,但它是可读的;而read(Double.class)
。
所以,这让我们回到了OP代码中的方法签名。但这有希望证明为什么你仍然希望保留这两种方法。现在来解决方法的内容:
由于Scanner.nextX()
不返回空值,因此可以将该方法重写为:
while (!scanner.hasNextX()) scanner.next();
return scanner.nextX();
因此,在这两种情况下复制它真的很容易:
private Integer readInteger() {
while (!scanner.hasNextInt()) scanner.next();
return scanner.nextInt();
}
private Double readDouble() {
while (!scanner.hasNextDouble()) scanner.next();
return scanner.nextDouble();
}
如果你愿意,你可以提出一种方法dropUntil(Predicate<Scanner>)
方法,以避免重复循环,但我不相信它真的会为你节省太多。
单个(近似)重复行在代码中比所有泛型和功能参数更轻松。它只是简单的旧代码,恰好比“新”编写方式简洁(并且可能更高效)。
这种方法的另一个优点是你不必使用盒装类型 - 你可以让方法返回int
和double
,而不必支付拳击税,除非你真的需要它。
这可能对OP没有好处,因为原始方法确实返回了盒装类型;我不知道这是真的需要,还是仅仅是编写循环方式的人工制品。但是,除非您真的需要它们,否则通常不会创建这些对象。
答案 5 :(得分:1)
不是理想的解决方案,但它仍然可以实现必要的重复代码删除,并且具有不需要Java-8的额外好处。
// This could be done better.
static final Scanner scanner = new Scanner(System.in);
enum Read{
Int {
@Override
boolean hasNext() {
return scanner.hasNextInt();
}
@Override
<T> T next() {
return (T)Integer.valueOf(scanner.nextInt());
}
},
Dbl{
@Override
boolean hasNext() {
return scanner.hasNextDouble();
}
@Override
<T> T next() {
return (T)Double.valueOf(scanner.nextDouble());
}
};
abstract boolean hasNext();
abstract <T> T next();
// All share this method.
public <T> T read() {
T v = null;
while (v == null) {
if ( hasNext() ) {
v = next();
} else {
scanner.next();
}
}
return v;
}
}
public void test(String[] args) {
Integer i = Read.Int.read();
Double d = Read.Dbl.read();
}
这有一些小问题,例如演员,但它应该是一个合理的选择。
答案 6 :(得分:0)
如果你不关心表现,反思是一种选择。
private <T> T read(String type) throws Exception {
Method readNext = Scanner.class.getMethod("next" + type);
Method hasNext = Scanner.class.getMethod("hasNext" + type);
T value = null;
while (value == null) {
if ((Boolean) hasNext.invoke(scanner)) {
value = (T) readNext.invoke(scanner);
} else {
scanner.next();
}
}
return value;
}
然后你打电话
Integer i = read("Int");