缩短if语句中包含.contains的语句

时间:2018-05-08 19:42:14

标签: java if-statement java-8

我想询问是否有不同的方法可以解决我所拥有的if-else样板情况。

每个ifelse if都有string.contains个比较。我使用的是Java 8.

有没有办法使用像x ? y : z这样的东西来缩短它?或者也许是开关?

if (s.contains("ServerAdmin ")) {
      data.setServerAdmin(s) // This happens everywhere
} else if (s.contains("DocumentRoot")) {
      data.setDocumentRoot(s) // This happens everywhere
} else if (s.contains("ServerName")) {
      data.setServerName(s) // This happens everywhere
} else if (s.contains("ErrorLog")) {
      data.setErrorLog(s) // This happens everywhere
} else if (s.contains("CustomLog")) {
      data.setCustomLog(s) // This happens everywhere
} else if (s.contains("<Directory")) {
      data.setDirectory(s) // This happens everywhere
}

有没有更好的方法来解决这个问题?

〜提前感谢您的任何帮助。

6 个答案:

答案 0 :(得分:6)

我能想到的Java最简单的解决方案是

Map<String, Consumer<String>> map = new LinkedHashMap<>();
map.put("ServerAdmin", data::setServerAdmin);
map.put("DocumentRoot", data::setDocumentRoot);
// ...
map.entrySet().stream()
    .filter(entry -> s.contains(entry.getKey()))
    .findFirst()
    .ifPresent(entry -> entry.getValue().accept(s));

使用LinkedHashMap保持比较顺序完整(如果相关),则只需搜索第一个contains并应用相应的函数

我想我会在实践中静态地声明地图,并将数据类的实例作为参数来保存每次执行的地图创建。例如,如下所示。

private static final Map<String, BiConsumer<Data, String>> MAP = new LinkedHashMap<>();
static {
    MAP.put("ServerAdmin", Data::setServerAdmin);
    MAP.put("DocumentRoot", Data::setDocumentRoot);
    // ...
}
public void foo(Data data, String s) {
    MAP.entrySet().stream()
        .filter(entry -> s.contains(entry.getKey()))
        .findFirst()
        .ifPresent(entry -> entry.getValue().accept(data, s));
}

答案 1 :(得分:2)

您可以创建一组魔术字符串及其相应的操作,然后迭代这些字符串,而不是复制大的if-elseif-else块。

private static final Map<String, BiConsumer<YourObj, String>> configActions;

static {
    Map<String, BiConsumer<YourObj, String>> tmp = new HashMap<>();
    tmp.put("ServerAdmin", YourObj::setServerAdmin);
    tmp.put("DocumentRoot", YourObj::setDocumentRoot);
    // ...
    configActions = Collections.unmodifiableMap(tmp);
}

public static YourObj load(List<String> directives) {
    YourObj config = new YourObj();
    directives.forEach(dir -> set(config, configActions, dir));
    return config;
}

static <T> void set(T obj, Map<String, BiConsumer<T, String>> setters, String value) {
    setters.entrySet().stream()
        .filter(e -> value.contains(e.getKey()))
        .findFirst()
        .ifPresent(e -> e.getValue().accept(obj, value));
}

通常,查找带有contains()的魔术字符串并不是一种非常强大的控制逻辑方法。如果字符串s遵循某种格式以确保您的搜索不会导致误报,请考虑解析该格式并提取令牌。如果输入字符串不遵循任何严格格式,则您很有可能首先登陆错误的令牌。例如,如果您的配置存储在YAML,XML或JSON文件中,请分别使用YAML,XML或JSON解析器。

作为一个例子,考虑如果你有令牌会发生什么事情&#34; LogFile&#34;和&#34; LogFileEncoding&#34;。 contain()&#34; LogFile&#34;,但只有一个是正确的匹配。

如果您使用的是Java 9或更高版本,则可以像这样初始化Map

private static final Map<String, BiConsumer<YourObj, String>> configActions = Map.of(
  "ServerAdmin", YourObj::setServerAdmin,
  "DocumentRoot", YourObj::setDocumentRoot,
  // ... 
);

如果您有十个以上的条目,则可以使用entry()构建器:

private static final Map<String, BiConsumer<YourObj, String>> configActions = Map.ofEntries(
  Map.entry("ServerAdmin", YourObj::setServerAdmin),
  Map.entry("DocumentRoot", YourObj::setDocumentRoot),
  // ...
);

这些methods were added用于解决缺少收集文字问题,并避免现有解决方法存在的问题。

答案 2 :(得分:1)

有点hacky。但如果你感觉好些,试试吧:

public class Data {
   private static Map<String, BiConsumer<Data, String>> SETTER_MAP = new LinkedHashMap<>(); // Use LinkedHashMap if you want to check the contains in order
   static {
       SETTER_MAP.put("ServerAdmin ", Data::setServerAdmin);
       ... your list goes on...
   }

   public void String setData(String s) {
     SETTER_MAP.entrySet().stream().filter(e -> s.contains(e.getKey())
                    .findFirst().ifPresent(e -> e.getValue().accept(this, s);
   }

}

答案 3 :(得分:1)

可能为了突出Java-8如何简化Java编码,我采取了基于Command模式OO的方法解决这个问题:

SELECT * 
FROM recuperacion.inventario a JOIN recuperacion.Salidas b 
ON a.codigo = b.codigo
WHERE a.codigo='5ae2399f4fbd3';

使用所有这些样板代码:

public interface Command {
    public void execute(Data data, String containerString);
}

public abstract class AbstractCommand implements Command {

  protected  String containeeString;
  protected AbstractBoundAction(String containeeString) {
    this.containeeString = containeeString;
  }

  protected abstract void action(Data data, String containerString);

  public void execute(Data data, String containerString) {
    if(containerString.contains(containeeString)) {
       action(data,containerString);
    }
  }
}
public class ServerAdminCommand extends AbstractCommand {
  public ServerAdminCommand() {
     super("ServerAdmin ");
  }

  protected void action(Data data, String containerString) {
     data.setServerAdmin(containerString);
  } 
}
public class DocumentRootCommand extends AbstractCommand {
  public DocumentRootCommand() {
     super("DocumentRoot");
  }

  protected void action(Data data, String containerString) {
     data.setDocumentRoot(containerString);
  } 
}
// And so on for the remaining data method invocation per contained strings

答案 4 :(得分:0)

我建议在s的任何内容中添加一个字段,并创建列出可能案例的enum:包含ServerAdmin,包含DocumentRoot等。调用此enum字段{ {1}} s出于我们的目的:

messageType

它更短,但保持可读性和直观性。

更新:刚看到switch (s.messageType){ case SERVER_ADMIN: data.setServerAdmin(s); break; case DOCUMENT_ROOT: data.setDocumentRoot(s); break; ... } s。除非由于其他原因使String成为您自己设计的对象,否则将其包装起来只是为了添加s(我不会这样做)可能并不值得。甚至延伸enum),所以我建议坚持String s。

答案 5 :(得分:-1)

你可以做得更多java-8-ish:

enum Operation implements Predicate<String> {
    SERVER_ADMIN {
        @Override
        public boolean test(String s) {
            return s.contains("ServerAdmin");
        }

        @Override
        public void accept(Data data, String input) {
            data.setServerAdmin(input);
        }
    };

    public abstract void accept(Data data, String input);
}


EnumSet.allOf(Operation.class)
            .stream()
            .filter(x -> x.test(s))
            .findAny()
            .ifPresent(x -> x.accept(data, s));