今天,我阅读了一些关于 Java 中的协变、逆变(和不变性)的文章。我阅读了英文和德文 Wikipedia 文章,以及 IBM 的其他一些博客文章和文章。
但我仍然对这些到底是什么感到有些困惑?有人说是关于类型和子类型之间的关系,有人说是关于类型转换,有人说它是用来决定一个方法是被覆盖还是被重载。
所以我正在寻找简单的英语解释,向初学者展示协方差和逆变(和不变性)是什么。加分是一个简单的例子。
最佳答案
Some say it is about relationship between types and subtypes, other say it is about type conversion and others say it is used to decide whether a method is overwritten or overloaded.
以上所有。
本质上,这些术语描述了子类型关系如何受到类型转换的影响。也就是说,如果 A和 B是类型,f是类型转换,并且 ≤ 子类型关系(即 A ≤ B 表示 A 是 B 的子类型),我们有
f如果 A ≤ B 是协变的表示 f(A) ≤ f(B) f如果 A ≤ B 是逆变的表示 f(B) ≤ f(A) f如果以上都不成立,则不变让我们考虑一个例子。让 f(A) = List<A>在哪里 List由
class List<T> { ... }
是 f协变、逆变还是不变?协变意味着 List<String>是 List<Object> 的子类型, 逆变为 List<Object>是 List<String> 的子类型并且不变量都不是另一个的子类型,即 List<String>和 List<Object>是不可转换的类型。在 Java 中,后者是正确的,我们说(有点非正式地)泛型是不变的。
另一个例子。让 f(A) = A[] .是 f协变、逆变还是不变?也就是说,String[] 是 Object[] 的子类型,Object[] 是 String[] 的子类型,还是两者都不是另一个的子类型? (答案:在Java中,数组是协变的)
这还是比较抽象的。为了更具体,让我们看看 Java 中的哪些操作是根据子类型关系定义的。最简单的例子是赋值。声明
x = y;
只有在 typeof(y) ≤ typeof(x) 时才会编译.也就是说,我们刚刚了解到这些语句
ArrayList<String> strings = new ArrayList<Object>();
ArrayList<Object> objects = new ArrayList<String>();
不会在 Java 中编译,但是
Object[] objects = new String[1];
会的。
另一个子类型关系很重要的例子是方法调用表达式:
result = method(a);
通俗地说,这个语句是通过赋值 a 来评估的。到方法的第一个参数,然后执行方法体,然后将方法返回值赋给result .与上一个示例中的普通赋值一样,“右手边”必须是“左手边”的子类型,即该语句只有在 typeof(a) ≤ typeof(parameter(method)) 时才有效和 returntype(method) ≤ typeof(result) .也就是说,如果方法声明为:
Number[] method(ArrayList<Number> list) { ... }
以下表达式都不会编译:
Integer[] result = method(new ArrayList<Integer>());
Number[] result = method(new ArrayList<Integer>());
Object[] result = method(new ArrayList<Object>());
但是
Number[] result = method(new ArrayList<Number>());
Object[] result = method(new ArrayList<Number>());
会的。
另一个子类型很重要的例子是覆盖。考虑:
Super sup = new Sub();
Number n = sup.method(1);
在哪里
class Super {
Number method(Number n) { ... }
}
class Sub extends Super {
@Override
Number method(Number n);
}
非正式地,运行时会将其重写为:
class Super {
Number method(Number n) {
if (this instanceof Sub) {
return ((Sub) this).method(n); // *
} else {
...
}
}
}
对于要编译的标记行,覆盖方法的方法参数必须是被覆盖方法的方法参数的父类(super class)型,返回类型必须是被覆盖方法的子类型。正式来说,f(A) = parametertype(method asdeclaredin(A))必须至少是逆变的,如果 f(A) = returntype(method asdeclaredin(A))必须至少是协变的。
请注意上面的“至少”。这些是任何合理的静态类型安全的面向对象编程语言都将强制执行的最低要求,但编程语言可能会选择更严格。在 Java 1.4 的情况下,重写方法时,参数类型和方法返回类型必须相同(类型删除除外),即 parametertype(method asdeclaredin(A)) = parametertype(method asdeclaredin(B))覆盖时。从 Java 1.5 开始,在覆盖时允许协变返回类型,即以下内容将在 Java 1.5 中编译,但在 Java 1.4 中不编译:
class Collection {
Iterator iterator() { ... }
}
class List extends Collection {
@Override
ListIterator iterator() { ... }
}
我希望我涵盖了所有内容 - 或者更确切地说,只是触及了表面。我仍然希望它有助于理解抽象但重要的类型变化概念。
关于java - 用简单的英语解释协变、不变和逆变?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8481301/
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url
我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht
我的工作要求我为某些测试自动生成电子邮件。我一直在四处寻找,但未能找到可以快速实现的合理解决方案。它需要在outlook而不是其他邮件服务器中,因为我们有一些奇怪的身份验证规则,我们需要保存草稿而不是仅仅发送邮件的选项。显然win32ole可以做到这一点,但我找不到任何相当简单的例子。 最佳答案 假设存储了Outlook凭据并且您设置为自动登录到Outlook,WIN32OLE可以很好地完成此操作:require'win32ole'outlook=WIN32OLE.new('Outlook.Application')message=
这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/