【问题标题】:Java compilation issue on Linux, using Windows specificLinux 上的 Java 编译问题,使用 Windows 特定
【发布时间】:2013-10-12 11:25:15
【问题描述】:

我在 Linux 下遇到了编译问题。 我在 Linux 上编译 java 程序;目标用途是 Linux 和 Windows。 代码检查是否存在特定于平台的类(如下面的代码所示)。 所以如果代码在Linux下运行,具体的Windows代码将不会被执行。

问题出现在使用平台特定类Win32MediaTray

报出的编译错误是

PrinterScanner.java:9: error: cannot find symbol
import sun.print.Win32MediaTray;
                 ^

在Linux下可以编译吗?还是只是不可能? 我可以使用一些解决方法(反思?) 不用说在windows下编译没有报错。

感谢您的帮助。

作为参考,这个问题背后的代码如下:

 private String getTrayName(Media media) {

    String result = "id:" + media.getValue();
    boolean isWin32 = media.getClass().getName().equals("sun.print.Win32MediaTray");
    if (isWin32) {
        Win32MediaTray w32 = (Win32MediaTray) media;
        result = result + ",winId:" + w32.winID;
    }
    return result;
}

【问题讨论】:

  • 告诉我们您的错误是什么。我可以猜到,但不想浪费我的时间。
  • 即使你可以让它编译,你希望它如何运行?这是一个特定于 Windows 的 API...
  • @rcook “不想浪费我的时间” 很好。我最近看到很多问题被人们试图猜测答案变成了“噪音”。
  • @JonSkeet 在“信息胜于推测”的基础上,我也想听听这个问题的答案。 OTOH(我的猜测),可能是 OP 正在编写应用程序。在不同平台上使用不同的行为(例如,“如果是 Windows,请将其放入媒体托盘......”)。
  • 谢谢你们的cmets。我希望现在这个问题是一个更好的问题

标签: java linux windows compilation cross-platform


【解决方案1】:

我相信您尝试使用的类是sun.print.Win32MediaTray

答案是你不能使用它......或编译一个使用它的类......在 Linux 版本的 Java 上。该类不包含在 Java 的 Linux 版本上的 rt.jar 文件中。

此外,您不应该使用它。 Java 文档非常清楚地表明,应用程序代码不应使用 sun.* 包层次结构中的类。


如果您别无选择,只能这样做,那么最好的办法是使用反射来获取该w32Id 字段的值。您还需要处理media 对象不是Win32MediaTray 类的实例的情况。请注意,您依赖于 Oracle 明确表示您不应该依赖的实现细节。它们有可能在未来的某些 Windows 版本中发生更改(恕不另行通知!)。

其他选择是:

  • 实现您自己的平台适配器类,并为每个平台使用不同的类。这些必须在每个平台上单独编译,然后动态加载。

  • 为每个平台实现单独的代码库。

【讨论】:

  • 我知道我不应该使用这个包。目前,此代码是唯一可以成功以编程方式为打印作业选择打印机托盘的工作代码(经过扩展研究)。如果您知道无需使用 sun.* 的更好方法来实现此目的,请告诉我。
【解决方案2】:

为了让编译器满意,您可以实现一个名为 sun.print.Win32MediaTray 的虚拟类,并使其在编译和运行时类路径中都可用。该类不需要工作,它只需要与 API 兼容(相同的签名和返回类型,但在这种情况下,您只需要扩展 Media 并拥有一个 public int winID),这样你就可以同时满足编译器和验证器。

在运行时,由于加载委托,rt.jar 中包含的版本应该加载到 Windows 上。在 Linux 上,只有虚拟版本可用,但您声明程序会检查平台并执行另一个代码分支,因此它不会导致您的程序失败。

例如,类路径上有以下类:

package sun.print;

import javax.print.attribute.standard.Media;

public class Win32MediaTray extends Media {
  public int winID = 0xBADC0DE;
  protected Win32MediaTray(int value) {
    super(value);
  }
  static {
    System.out.println("Won't see me on Windows");
  }
}

我设法在 Windows 上运行了这个程序:

public class Main {
  public static void main(String[] args) {
    PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
    for  (PrintService svc : services ) {
      DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PAGEABLE;
      Object o = svc.getSupportedAttributeValues(Media.class, flavor, null);
      if (o != null && o.getClass().isArray()) {
        for (Media media : (Media[]) o) {
          if ( media instanceof Win32MediaTray )
            System.out.println( ((Win32MediaTray) media).winID );
        }
      }
    }
  }
}

静态初始化程序中的消息不会在 Windows 上打印,因为实际加载的定义是来自 rt.jar 的定义。显然,代码可以在任何平台上编译。

【讨论】:

  • 有趣的想法,如何制作这样的课程?最后我用反射做到了
【解决方案3】:

如何将使用 Windows 特定内容的代码放入单独的 jar 中;然后您可以在 Windows 上编译并包含该 jar,否则将其保留在系统之外。

执行此操作的一种标准方法是让您的应用程序代码使用一个或多个接口;您可以让工厂提供实现类或使用 Spring 或其他方式注入它们。但我认为,您的问题应该是“我在一个针对多个操作系统的应用程序中有这种 Windows 依赖项,我该如何处理它?”而不是“我如何在 Linux 上编译它”。

【讨论】:

  • 添加另一个 jar/接口/实现将是一个过于繁重的解决方案。我会选择反思的方式。
  • 这怎么比使用反射更“重”?
  • 它比较重,因为我需要在 Linux 和 Windows 之间分叉构建链。添加一个新的 jar/项目。从另一个 jar/项目中使用它。检测何时使用 jar
  • 好吧,当然,您自己决定更容易/更好的解决方案是什么。这个解决方案是否真的更容易取决于你对反射所做的事情,我们从这个问题中不知道,不需要。但是,如果您因为构建工具使更简单的解决方案变得更加困难而采用 java 反射(或任何主要技术),那么您应该考虑是否拥有正确的构建工具。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-13
  • 1970-01-01
  • 1970-01-01
  • 2018-06-29
  • 2020-08-29
相关资源
最近更新 更多