首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >接口中的Java 9默认方法:从哪些模块调用它们?

接口中的Java 9默认方法:从哪些模块调用它们?
EN

Stack Overflow用户
提问于 2018-03-06 14:55:29
回答 2查看 339关注 0票数 6

假设您有moduleA和moduleB。ModuleA定义了一个接口(例如服务),ModuleB有一个具体的类来实现接口(提供服务)。

现在,如果接口有一个默认方法,并且您在moduleB中的类上调用它(来自另一个模块),那么这个调用应该在moduleA或moduleB中执行吗?显然是来自moduleA ..。理由是什么?

示例:假设您有一个代码来执行以下操作:

代码语言:javascript
复制
InputStream is = this.getClass().getResourceAsStream(fullPath);

如果此代码位于moduleB中服务的实现中,则将打开流。但是,如果代码位于moduleA中的默认方法中,那么当在moduleB上调用服务时,您将需要在moduleB中拥有一个“开放”资源(因此调用似乎认为它来自“外部”moduleB)。

我想了解一下原因。

谢谢

用一个例子编辑我的问题。假设在moduleA中有以下代码:

代码语言:javascript
复制
public interface PropertiesProvider {
    public default Properties get(String domain) {
        Class clazz =this.getClass();
        System.out.println(" CLASS " +clazz);
        InputStream is = clazz.getResourceAsStream(domain) ;
        if (is != null) {
            Properties props = new Properties();
            try {
                props.load(is);
                return props;
            } catch (IOException e) {
                //log
            }
        }
        return null;
    }

}

在moduleB中

代码语言:javascript
复制
public class PropertiesProviderImpl implements PropertiesProvider {}

如果从ModuleA调用服务,则会跟踪调用来自PropertiesProviderImpl类的调用,查找资源,但如果没有“打开”,则不会加载资源。

如果将代码复制到PropertiesProviderImpl中,则会跟踪对同一个类的调用,找到重新源并加载它,即使它不是“打开的”。

所以我的问题是:为什么调用来自同一个类?(区别在于,在一种情况下,该方法是从接口中的默认方法继承的)

EN

回答 2

Stack Overflow用户

发布于 2018-03-08 15:00:01

查看getResourceAsStream If this class is in a named Module then this method will attempt to find the resource in the module.的文档

在第一种情况下,您的代码(在moduleA中)可以看到Type,但是不能看到实现Type的类,因为它在moduleB中。在第二种情况下,您的代码可以看到“实现”Type的类。

看下面的参考资料,最重要的句子是:

在模块化设置中,只要包含提供者类的包为上下文类加载器所知,类::forName的调用就会继续工作。但是,通过调用提供者类的构造函数--反射newInstance方法--将无法工作:提供程序可能是从类路径加载的,在这种情况下,它将位于未命名模块中,或者在某个命名模块中,但在这两种情况下,框架本身都在java.xml模块中。该模块只依赖并因此读取基本模块和,因此框架无法访问任何其他模块中的提供程序类。

..。

相反,修改反射API,只需假设反映某种类型的任何代码都在一个模块中,该模块可以读取定义该类型的模块.

长答案反光可读性

框架是一种在运行时使用反射加载、检查和实例化其他类的工具. 给定在运行时发现的类,框架必须能够访问它的一个构造函数才能实例化它。然而,就目前情况而言,情况通常并非如此。 该平台的流XML解析器,例如,加载和实例化系统属性javax.xml.stream.XMLInputFactory命名的javax.xml.stream.XMLInputFactory服务的实现(如果定义的话),而不是通过ServiceLoader类发现的任何提供者。忽略代码读取的异常处理和安全检查,大致如下:

代码语言:javascript
复制
String providerName
    = System.getProperty("javax.xml.stream.XMLInputFactory");
if (providerName != null) {
    Class providerClass = Class.forName(providerName, false,
                                        Thread.getContextClassLoader());
    Object ob = providerClass.newInstance();
    return (XMLInputFactory)ob;
}
// Otherwise use ServiceLoader
...

在模块化设置中,只要上下文类加载器知道包含提供者类的包,Class::forName的调用就会继续工作。但是,通过反射newInstance方法调用提供程序类的构造函数是行不通的:提供程序可能是从类路径加载的,在这种情况下,它将位于未命名模块中,或者可能在某个命名模块中,但在这两种情况下,框架本身都在java.xml模块中。该模块只依赖于并因此读取基本模块,因此框架将无法访问任何其他模块中的提供程序类。 为了使框架能够访问提供者类,我们需要使提供程序的模块被框架的模块读取。我们可以要求每个框架在运行时明确地为模块图添加必要的可读性边缘,就像本文档的早期版本一样,但是经验表明,这种方法很麻烦,也是迁移的障碍。 因此,我们只是修改反射API,假设反映某种类型的任何代码都位于可以读取定义该类型的模块的模块中。这使得上面的示例和其他类似的代码能够在不改变的情况下工作。这种方法并不会削弱强大的封装:公共类型必须仍然在导出的包中,这样才能从其定义模块之外访问,无论是从编译的代码还是通过反射。

票数 3
EN

Stack Overflow用户

发布于 2018-03-12 10:01:23

由于我们不完全理解之前的反应,所以我们进行了一些额外的测试

在每个测试中,资源文件都不会被“打开”。

1)调用clazz.getResouceAsStream的代码是定义服务的默认接口方法。实现接口的类不定义任何方法。

-> this.getClass()生成实现类,测试无法找到资源

2)我们在默认方法中添加了此代码

对象obj = clazz.getConstructor().newInstance();

是的,它失败了

3)我们更改了代码,因此PropertiesProvider是抽象类,PropertiesProviderImpl是从它继承的。

同样的行为。

因此,是的,这意味着如果继承或直接调用相同的代码,其行为将有所不同。这是令人担忧的:这意味着语言的内部逻辑将导致复杂的拜占庭行为(我们抛弃C++的原因)。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49133735

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档