避免在 Java 中使用多个 If 语句

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1298672/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 16:01:05  来源:igfitidea点击:

Avoiding multiple If statements in Java

java

提问by jai

I've coded a method something like this. But I guess this should undergo refactoring. Can any one suggest the best approach to avoid using this multiple if statements?

我已经编写了一个类似这样的方法。但我想这应该进行重构。任何人都可以建议避免使用多个 if 语句的最佳方法吗?

private String getMimeType(String fileName){
  if(fileName == null) {
    return "";   
  } 
  if(fileName.endsWith(".pdf")) {
    return "application/pdf";   
  }
  if(fileName.endsWith(".doc")) {
    return "application/msword";  
  }
  if(fileName.endsWith(".xls")) {
    return "application/vnd.ms-excel"; 
  }
  if(fileName.endsWith(".xlw")) {
    return "application/vnd.ms-excel"; 
  }
  if(fileName.endsWith(".ppt")) {
    return "application/vnd.ms-powerpoint"; 
  }
  if(fileName.endsWith(".mdb")) {
    return "application/x-msaccess"; 
  }
  if(fileName.endsWith(".rtf")) {
    return "application/rtf"; 
  }
  if(fileName.endsWith(".txt")) {
    return "txt/plain"; 
  }
  if(fileName.endsWith(".htm") || fileName.endsWith(".html")) {
    return "txt/html"; 
  }
  return "txt/plain"; 
}

I cannot use switch-case here as my 'condition' is a java.lang.String.

我不能在这里使用 switch-case,因为我的“条件”是java.lang.String.

回答by Joachim Sauer

You can use a Mapto hold your solutions:

您可以使用 aMap来保存您的解决方案:

Map<String,String> extensionToMimeType = new HashMap<String,String>();
extensionToMimeType.put("pdf", "application/pdf");
extensionToMimeType.put("doc", "application/msword");
// and the rest

int lastDot = fileName.lastIndexOf(".");
String mimeType;
if (lastDot==-1) {
    mimeType = NO_EXTENSION_MIME_TYPE;
} else {
    String extension = fileName.substring(lastDot+1);
    mimeType = extensionToMimeType.get(extension);
    if (mimeType == null) {
        mimeType = UNKNOWN_EXTENSION_MIME_TYPE;
    }
}

For this code to work you'll need to have defined NO_EXTENSION_MIME_TYPEand UNKNOWN_EXTENSION_MIME_TYPEas in your class, somewhat like this:

要使此代码正常工作,您需要像在类中一样定义NO_EXTENSION_MIME_TYPEUNKNOWN_EXTENSION_MIME_TYPE,有点像这样:

private static final String NO_EXTENSION_MIME_TYPE = "application/octet-stream";
private static final String UNKNOWN_EXTENSION_MIME_TYPE = "text/plain";

回答by Kezzer

Using a HashMapperhaps?

也许使用HashMap

This way you could do myMap.get(mystr);

这样你可以做 myMap.get(mystr);

回答by Andreas Dolk

Personally I don't have problems with the if statements. The code is readable, it took just milliseconds to understand what you're doing. It's a private method anyway and if the list of mime types is static then there's no urgent need to move the mapping to a properties file and use a lookup table (map). Map would reduce lines of code, but to understand the code, then you're forced to read the code and the implementation of the mapping - either a static initializer or an external file.

我个人对 if 语句没有问题。代码是可读的,只需几毫秒就可以理解你在做什么。无论如何,它是一个私有方法,如果 mime 类型列表是静态的,那么没有迫切需要将映射移动到属性文件并使用查找表(映射)。Map 会减少代码行数,但要理解代码,您必须阅读代码和映射的实现——静态初始化程序或外部文件。

You couldchange the code a bit and use an enum:

可以稍微更改代码并使用枚举:

private enum FileExtension { NONE, DEFAULT, PDF, DOC, XLS /* ... */ }

private String getMimeType(String fileName){
  String mimeType = null;

  FileExtension fileNameExtension = getFileNameExtension(fileName);

  switch(fileNameExtension) {
    case NONE:
      return "";
    case PDF:
      return "application/pdf";

    // ...

    case DEFAULT:
      return "txt/plain";   
  }

  throw new RuntimeException("Unhandled FileExtension detected");
} 

The getFileNameExtension(String fileName)method will just return the fitting enum value for the fileName, FileExtension.NONEif fileName is empty (or null?) and FileExtension.DEFAULTif the file extension is not mapped to a mime type.

如果 fileName 为空(或 null?)并且文件扩展名未映射到 mime 类型getFileNameExtension(String fileName)FileExtension.NONE则该方法将只返回适合 fileName 的枚举值FileExtension.DEFAULT

回答by dfa

what about using a MIME detection libraryinstead?

改用MIME 检测库怎么样?

  • mime-util
  • mime4j
  • JMimeMagic library - Free. Uses file extension and magic headers to determine MIME type.
  • mime-util - Free. Uses file extension and magic headers to determine MIME type.
  • DROID (Digital Record Object Identification) - Free. Uses batch automation to detect MIME types.
  • Aperture Framework - Free. A framework for crawling external sources to identify MIME types.
  • mime-util
  • mime4j
  • JMimeMagic 库 - 免费。使用文件扩展名和魔术头来确定 MIME 类型。
  • mime-util - 免费。使用文件扩展名和魔术头来确定 MIME 类型。
  • DROID(数字记录对象识别) - 免费。使用批处理自动化来检测 MIME 类型。
  • Aperture 框架 - 免费。用于抓取外部资源以识别 MIME 类型的框架。

(feel free to add more, there so many libraries..)

(随意添加更多,有这么多图书馆..)

回答by Pokot0

I consider your approach to be the best overall. This comes after having tested with a number of different approaches myself.

我认为你的方法是最好的整体。这是在我自己使用多种不同方法进行测试之后得出的。

I see a number of huge benefits in your current approach, namely:

我在您当前的方法中看到了许多巨大的好处,即:

  1. Easily readable and understandableby anyone (in my experience, medium-level programmers often underestimate this and usually prefer going with fancy-patterns which, in the end are not readable at all for the vast majority of programmers who do not know that specific pattern)
  2. All the information is in one single place. As Andreas_D pointed out, hunting around files or classes is not a good option for someone that needs to fix a bug while you are on holiday!
  3. Easily maintainable: I could "F3" (if you are Eclipse-ing) on the method and add a new content type in seconds without any worries of introducing bugs!
  1. 任何人都易于阅读和理解(根据我的经验,中级程序员经常低估这一点,并且通常更喜欢使用花哨的模式,而对于大多数不了解特定模式的程序员来说,最终根本无法阅读)
  2. 所有信息都集中在一个地方。正如 Andreas_D 指出的那样,对于需要在假期中修复错误的人来说,寻找文件或类并不是一个好的选择!
  3. 易于维护:我可以在该方法上按“F3”(如果您正在使用 Eclipse)并在几秒钟内添加新的内容类型,而无需担心引入错误!

I can suggest a few things anyway:

无论如何,我可以提出一些建议:

  1. This method is very general purpose: Why should it be private?! This is a public methodof some utility/helper class! Moreover it should be a static method!! You don't need anything from the Object itself to perform your job!
  2. You could use indentingto make things prettier and compact. I know that indenting is some kind of religion for the most of us, but I think it should not be a strict rule; it should be properly used to make our code more readable and compact. If this would be a config file you would probably have something like:
  1. 这个方法很通用:为什么要私有?!这是一些实用程序/助手类的 公共方法!而且它应该是一个静态方法!!您不需要对象本身的任何东西来执行您的工作!
  2. 您可以使用缩进使事情更漂亮和紧凑。我知道缩进对我们大多数人来说是一种信仰,但我认为这不应该是一个严格的规则;应该正确使用它来使我们的代码更具可读性和紧凑性。如果这将是一个配置文件,您可能会有类似的内容:
pdf=application/pdf
doc=application/msword
pdf=application/pdf
doc=application/msword

You could have a very similar result with:

您可能会得到非常相似的结果:

    public static String getMimeType(String fileName){
       if(fileName == null) return "";
       if(fileName.endsWith(".pdf")) return "application/pdf";
       if(fileName.endsWith(".doc")) return "application/msword";
       if(fileName.endsWith(".xls")) return "application/vnd.ms-excel"; 
       return "txt/plain"; 
   }

This is also what a lot of the Map based implementations look like.

这也是许多基于 Map 的实现的样子。

回答by nxhoaf

Command pattern is the way to go. Here is one example using java 8:

命令模式是要走的路。下面是一个使用 java 8 的例子:

1. Define the interface:

1.定义接口:

public interface ExtensionHandler {
  boolean isMatched(String fileName);
  String handle(String fileName);
}

2. Implement the interface with each of the extension:

2. 用每个扩展实现接口:

public class PdfHandler implements ExtensionHandler {
  @Override
  public boolean isMatched(String fileName) {
    return fileName.endsWith(".pdf");
  }

  @Override
  public String handle(String fileName) {
    return "application/pdf";
  }
}

and

public class TxtHandler implements ExtensionHandler {
  @Override public boolean isMatched(String fileName) {
    return fileName.endsWith(".txt");
  }

  @Override public String handle(String fileName) {
    return "txt/plain";
  }
}

and so on .....

等等 .....

3. Define the Client:

3. 定义客户端:

public class MimeTypeGetter {
  private List<ExtensionHandler> extensionHandlers;
  private ExtensionHandler plainTextHandler;

  public MimeTypeGetter() {
    extensionHandlers = new ArrayList<>();

    extensionHandlers.add(new PdfHandler());
    extensionHandlers.add(new DocHandler());
    extensionHandlers.add(new XlsHandler());

    // and so on

    plainTextHandler = new PlainTextHandler();
    extensionHandlers.add(plainTextHandler);
  }

  public String getMimeType(String fileExtension) {
    return extensionHandlers.stream()
      .filter(handler -> handler.isMatched(fileExtension))
      .findFirst()
      .orElse(plainTextHandler)
      .handle(fileExtension);
  }
}

4. And this is the sample result:

4. 这是示例结果:

  public static void main(String[] args) {
    MimeTypeGetter mimeTypeGetter = new MimeTypeGetter();

    System.out.println(mimeTypeGetter.getMimeType("test.pdf")); // application/pdf
    System.out.println(mimeTypeGetter.getMimeType("hello.txt")); // txt/plain
    System.out.println(mimeTypeGetter.getMimeType("my presentation.ppt")); // "application/vnd.ms-powerpoint"
  }

回答by Zed

There is no way to evade that in general. In your case - if there is a set of allowed extensions - you could create an Enum, convert the extension to the Enum type via valueOf(), and then you can switch over your enum.

一般来说,没有办法回避这一点。在您的情况下 - 如果有一组允许的扩展名 - 您可以创建一个 Enum,通过 valueOf() 将扩展名转换为 Enum 类型,然后您可以切换您的枚举。

回答by BalusC

Easiest and shortest way for this particular problem would be using the builtin Java SE or EE methods.

解决这个特定问题的最简单和最短的方法是使用内置的 Java SE 或 EE 方法。

Either in "plain vanilla" client application (which derives this information from the underlying platform):

在“普通”客户端应用程序(从底层平台获取此信息)中:

String mimeType = URLConnection.guessContentTypeFromName(filename);

Or in a JSP/Servlet web application (which derives this information from the web.xmlfiles):

或者在 JSP/Servlet Web 应用程序中(从web.xml文件中获取此信息):

String mimeType = getServletContext().getMimeType(filename);

回答by Jesper

I would do this by putting the associations in a map, and then using the map for lookup:

我会通过将关联放在地图中,然后使用地图进行查找来做到这一点:

Map<String, String> map = new HashMap<String, String>();

map.put(".pdf", "application/pdf");
map.put(".doc", "application/msword");
// ... etc.

// For lookup:
private String getMimeType(String fileName) {
    if (fileName == null || fileName.length() < 4) {
        return null;
    }

    return map.get(fileName.substring(fileName.length() - 4));
}

Note that using the switchstatements on strings is one of the proposed new features for the next version of Java; see this pagefor more details and an example of how that would look in Java 7:

请注意,switch在字符串上使用语句是下一版本 Java 建议的新功能之一;有关更多详细信息以及在 Java 7 中的外观示例,请参阅此页面

switch (fileName.substring(fileName.length() - 4)) {
    case ".pdf": return "application/pdf";
    case ".doc": return "application/msword";
    // ...
    default: return null;

(edit: My solution assumes the file extension is always 3 letters; you'd have to change it slightly if it can be longer or shorter).

(编辑:我的解决方案假设文件扩展名始终为 3 个字母;如果它可以更长或更短,您必须稍微更改它)。

回答by aberrant80

Create an enum called MimeType with 2 String variables: extension and type. Create an appropriate constructor and pass in the ".xxx" and the "application/xxx" values. Create a method to do the lookup. You can use enums in switch.

创建一个名为 MimeType 的枚举,其中包含 2 个字符串变量:扩展名和类型。创建适当的构造函数并传入“.xxx”和“application/xxx”值。创建一个方法来进行查找。您可以在 switch 中使用枚举。