1.看美剧学英文是胡扯?错,你只是没有按这四个步骤来
经常有学生告诉我,听说可以通过看美剧学英语,轻松又有趣,结果自己刷了一部剧,除了重要的剧情,什么也没记住。
学生小曾就是“美剧学英语”派的忠实拥护者。她刷了很多美剧,从律政题材的《傲骨贤妻》、刑侦题材的《犯罪现场调查(CSI)》,到奇幻题材的《吸血鬼日记》和《权力的游戏》,她说并不是没有收获,她感觉自己听到英语更亲切、更顺耳了。
除此之外,她能回忆起一些简单的打招呼的用语,例如,“Hi, how are you doing”,事实上她能立刻回忆起的主要是故事情节以及人物感情关系,她能把这个故事讲得栩栩如生。
到底学了多少英语?她掰着指头都能列举出来。更糟的是,如果一段时间不刷美剧,之前学到的一些英语表达又忘了。
小曾的情况符合心理学上的“衰减器理论”,这是由心理学家特瑞斯曼提出的,该理论主张:当外界输入的信息通过过滤装置时,被注意或被追随的信息能完全通过,不被注意或非追随的信息也能通过,但在强度上出现衰减。该理论同时指出,对个体有重要意义的信息容易被激活并接受进一步的信息加工。
根据该理论,小曾在观看美剧时,对于她而言更有意义的部分是弄清剧情、理清人物关系,因此这部分信息被注意、被追随,而关于语言本身的部分则由于不被注意而出现衰减。
一、为什么看美剧时学生很难学习到英语?
1.英语语言部分转瞬即逝,没有得到强化从而被遗忘
学生在看美剧时,英文的发音及屏幕下方的英文字幕都处于高速流逝状态。根据上文提到的“衰减器理论”,英语语言部分本身因不被注意而出现衰减,最终只能形成心理学上的“短时记忆”。
短时记忆的保持时间最长不超过1分钟,可以通过复述来保持信息,但如果不复述,那么很快会被遗忘。
我们在看美剧时,为了保持剧情的连贯性,也急于想知道后面发生了什么事,基本不会去复述听到的或看到的英文词句,那么英语语言的部分只能成为短时记忆并很快被遗忘。
《教育心理学》认为,长时记忆中的信息能否得到有效的保持和提取,取决于学生在学习过程中对信息的加工水平。
由心理学家克雷克和洛克哈特提出的加工水平理论主张,最低水平的加工是感觉加工,比如识别特征;较高水平的加工是识别模式;最高水平的加工是语义提取。同时,该理论主张,如果一个刺激较长时间呈现在个体面前,就可能得到较高水平的加工。
美剧中的英语发音以及英文字幕文本并没有较长时间呈现在学生眼前,自然无法得到较高水平的加工,至多处于最低水平的加工,比如看见英文字幕此时出现一个简单句。没有较高水平的信息加工,就无法获得长时记忆,那么也就无法真正学习到英语。
2.相比起剧情,英语语言是更难“下咽”的部分
心理学家爱德华·琼斯和史蒂文·伯格拉斯在上个世纪七十年代提出了自我妨碍的概念:在表现情境中,个体为了回避或降低因不佳表现所带来的负面影响,会采取任何能够增大将失败原因外化机会的行动和选择。
《教育心理学》指出,在学业上,这种自我妨碍表现为回避可能出现的消极情感结果而采取降低或放弃努力的行为。
学生小曾按照我的建议试着复述英文字幕,她在复述的时候,刚好她的室友经过,笑着说了句:“这样看美剧一定能学到很多的英语吧。”小曾立刻心虚了,心想着要是这样还是学不好,同学会不会怀疑她的能力有问题。这样一想,她放弃了,告诉室友:“我就是试着玩儿,再说了,剧情这么紧张,怎么可能一直暂停。”
首先,小曾害怕付出努力得不到相应的回报,更怕室友因此对她的评价降低,因此放弃继续尝试,同时将自己放弃尝试的原因归结为“剧情紧张”,是典型的自我妨碍表现。
此外,斯坦福心理学教授卡罗尔·德韦克提出,有表现目标而又缺乏自信的学生往往会选择简单任务以避免表现出无能。当遇到困难时,这样的学生倾向于采取自我防范的策略,从而避免给他人造成低能的印象,或者干脆放弃不再努力。
另一个学生小周,发过一篇演讲稿给我,请我帮忙修改,并且针对英文演讲给出一些建议。小周的演讲稿结构没有问题,我只修改了个别表达方式,然后告诉他,对着镜子多练习,最好能够脱稿演讲,展现出自己的风采。
事后,我问起小周演讲的效果,他回答说:“我练习的时候是试过脱稿演讲,但总是会忘一些内容,所以还是带着稿子去念了。真脱稿的话,万一中间卡壳,那不是丢人丢大了。”小周渴望在演讲时有优异的表现,但又缺乏信心,所以最后采取“照着稿子念”这一自我防范策略,以规避造成消极印象的风险。
这个道理也同样适用于看美剧时的状态,学生的初衷是美好的,希望通过多刷美剧学好英语,但一旦发现需要暂停复述,复述之后也未必能学好,那么学习的信心随之下降,因而在跟别人讨论时依然说的是剧情和人物,因为回忆剧情是相对简单的任务,同时也能避免他人问起自己的学习效果。
二、到底怎么做才能真正通过美剧学到英语?
步骤一:选择适合学习的美剧,不是最有兴趣的美剧
《6个月学会任何一种外语》一书指出,科学研究已经表明对个人有重要意义的事情或信息能够比较容易吸引一个人高质量的注意力,比如跟生存、爱情有关的信息。
也就是说,如果我们想要让自己的注意力集中在语言部分,那么语言内容最好是与我们自己息息相关的,比如日常生活。试着对比以下两组美剧台词:
What makes you think you belong here? With all due respect, I thought the key to being a lucid crime scene investigator was to reserve judgment until the evidence vindicates or eliminates assumption. (是什么让你觉得你属于这里?在所有应有的尊重,我认为成为一个清醒的犯罪现场调查员的关键是保留判决,直到证据证明或消除假设。)
——《犯罪现场调查》
But when her doctor announced Lynette was pregnant, her husband Tom had an idea: why not quit your job? Kids do much better with stay-at-home moms; it was much less stressful. (但是当她的医生宣布Lynette怀孕的时候,她的丈夫Tom有了一个主意:为什么不辞掉你的工作呢?有全职妈妈的孩子表现更好;压力要小得多。)
——《绝望的主妇》
哪一组更能吸引你的注意力呢?除非学生是法律相关专业,其他专业的普通学生很难关注第一组英文台词,因为信息与自身的关联性太弱,而第二组台词信息却是我们日常生活中常见的现象,不少妈妈生宝宝后,为了照顾宝宝,会选择全职带娃。后一组英文文本无疑更能获得学生高质量的注意力。
类似地,认知教育心理学家奥苏贝尔认为,要使学习有价值就应该让学习有意义,意义学习产生的条件是新学的内容能够与已有的知识结构联系起来,即学习的内容对于个体来说有意义。
因此,在选择美剧题材的时候,应该尽量避免与自身关联性很弱的题材,比如《邪恶力量》这样的灵异类题材;相反,如果你是法律系的学生,那么《傲骨贤妻》将是不错的选择。
此外,美国教育学家克拉申提出了“可理解性输入”的理论,可以帮助学生选择难度合适的美剧。该理论主张,当语言习得者接触到略高于他现有语言技能水平的第二语言输入,又能理解信息内容及其意义才能产生习得。
从以上《犯罪调查现场》和《绝望主妇》的台词对比可以看出,前者台词涉及到较难的单词,比如lucid, vindicate等,就算经过查词翻译成中文,没有相关的背景知识,学生也看不懂是什么意思,这样的英文输入是无益于学生吸收的。
综上,学生在选择美剧作为学习材料时,应遵循以下两个标准:
第一,美剧题材与自己息息相关,比如学习传播学的同学,可以选择美剧《公关》;倘若自己所学领域没有评分较高的美剧,那么可以选择生活类美剧,比如《绝望的主妇》、《美少女的谎言》等,后者主要讲述美国校园悬疑故事。
第二,试看一集,测试自己能否听懂或看懂80%以上的英文,倘若大量生词出现,且连中文字幕也无法理解,那么不适合用来学习。如果可理解的内容在80%及以上,那么有利于习得。
步骤二:不是机械地复述,而是真实地模仿
社会认知理论认为,大多数的人类行为可以通过观察学习与模仿获得。《6个月学会任何一种外语》也认为,想要学好任何语言的发音,需要多听和模仿,且模仿的关键在于英语母语者的面部和口型,也就是“看脸说话”。
不少学生也试过复述,但效果总是不理想,原因就在于仅仅是盯着英文字幕念,就算发音不准确,自己也很难找到原因。通过观察美剧人物的面部肌肉运动及口型,能够帮助学生纠正发音。
比如学生常发错的音【th】,发音时需要将舌尖轻轻放在上下排牙齿之间,才能正确发出thank, think, these等单词;再比如长元音【i:】,需要将嘴裂开,上下牙齿合拢,如果发音不清晰,beach(沙滩)这个词很容易与另一个负面意思的单词相混淆。因此,在无人指导的情况下,观察美剧人物的口型能够自我纠音。
此外,《6个月学会任何一种外语》建议学习者通过创造和运用画面来学习外语,也就是说,将外语的发音和文本与个体的感觉、听觉、画面等联结起来。
比如在《绝望的主妇》中有这样一句台词:It's been a little rough.
我们在学习时可以将这个表达与剧情和画面“锁”在一起。女主角之一的Lynette本来是职场女强人,后来听丈夫的话呆在家里做全职妈妈,丈夫则飞来飞去各地开会工作。这一次丈夫出差好几天,Lynette独自照顾三个孩子,实在疲惫不堪,好不容易等回了丈夫,这时Lynette满脸憔悴地说:It's been a little rough,表明自己确实很累、疲于应付,过得有些艰难。
在复述这个句子的时候,可以将自己想象成女主Lynette,然后模仿她的语气和语调说出这句台词,复述的时候尽量在脑海中呈现出剧情画面。下一次,当你面对一桌子待处理的工作或一堆要复习的资料时,不妨抓着脑袋大喊一声:It's been a little rough。
步骤三:整理学习到的英文表达,通过二次加工加深印象
在教育心理学上有一种学习策略叫做“组织策略”,指的是对学习材料进行归类整理,使信息之间建立类别、层次或结构关系,从而促进对学习材料的理解、记忆和表述。
组织策略告诉我们,如果对学习到的英语内容进行整理、归类,有助于我们记忆和再次提取出学过的英文表达。
步骤一建议选择与生活息息相关的美剧,另一个原因是在这些美剧中,人物言谈中涉及到的表达非常高频,因为多是与生活场景相关的短语和句子,比如家庭聚会、购物、干洗、上班、邻里和睦等,因此我们的归类以生活中常见的“打招呼”和“安慰”为例。
此外,还可以按照场景归类,比如购物场景,下面可分为“寻求店员帮助”、“结账”等子类别;图书馆学习场景,下面可分为“查询书籍”、“借书”、“还书”、“续借”等子类别。
步骤四:将学习到的地道表达运用在真实的生活场景里
学习理论中的迁移理论告诉我们,一种学习能够对另一种学习产生影响,或者已获得的知识经验能够对完成其他活动产生影响。
因此,我们通过步骤二的“真实模仿”和步骤三的“组织策略加工”学习到的英语可以迁移到相似的生活场景里。比如在校园里遇见熟悉的同学,我们可以非常自然随意的来一句“hey, what's up”。
这种迁移被美国心理学家加涅称为水平迁移,指的是同一概括水平的经验之间的相互影响,学习内容之间是并列的逻辑关系。
同理,心理学家巴奈特和塞西提出了功能性的迁移线索,与在何处使用何种技能的决策以及对思维定式的唤起有关。具体来说,是否能够把在学校里学到的东西应用到现实生活中去。
因此,《教育心理学》建议在教学中引入“真实性任务”,也就是学生在课堂之外将要面对的与现实生活相关的问题,这类问题能够比较容易地让学生认识到学习任务的实用价值,从而发现学习任务的意义并激发兴趣。
所以,我们通过美剧学习英语的最后一步就是要运用到真实的生活场景里。步骤三已为我们做好了场景准备,面对真实性问题,比如“怎么打招呼”、“怎么安慰失意的朋友”、“买东西怎么结账”、“图书馆的书怎么续借”等等,我们已经有了语料储备。
接下来让我们一起来实战,以下是超市结账时发生的英文对话:
You: Hi. Check out, please.
Staff: Sure. The charge is 50 dollars in total.
You: Can I pay by credit card? I don't have enough cash on me.
Staff: Yes. Swipe your card here, please.
You: Here we go.
Staff: Thank you. Have a nice day.
You: You too.
对话里加粗的短语和句子正是在美剧生活场景里常见的表达,我们只需要真实地模仿、看人物嘴型纠正发音、归类整理、水平迁移至自己的生活场景,那么我们就能真正通过美剧习得英语。
如果你有计划出国旅行,那么一定要抓住机会,在各个场景下去应用学到的英文表达。在没有语言环境的情况下,我们可以寻找一位志同道合的语伴来模拟场景对话,或者在真实场景下,嘴里说中文,心里用英文回答。
2."a wild card" 是什么意思?~名师答疑
30 September 2022
美国朋友常说"a wild card."它是什么意思?
这种表达在美式英语的牌游戏中很多,尤其在扑克牌。
每套牌中都有两张多余的牌。这些可以代替丟失或损坏的牌。这些叫作"百搭牌。"它们常常有个小丑的图案。有时候也叫小丑。
在牌游戏中,"Jokers."牌可以与任何牌搭。如我有三个王和一个Jokers.我将用这个百搭牌作王,所以现在我有四个王。
但是我们不只在牌游戏中听到"wild card."在运动中用这种表达表示一个被邀请竞争冠军的队。如:
For example, in American football, the best teams play each other at the end of the season – in a series of games called "playoffs." A few teams that might not have the best records are invited to play against the top teams. It is possible, if the wildcard team is good enough, that it can win the championship.
Here is an example showing how a wild card team won the championship of American football, the Super Bowl.
In 2007, the New York Giants entered the playoffs as a wild card team, but they went on to beat the New England Patriots and win the Super Bowl.
你可能会猜测我们也能用"a wild card."描述人。如果一个人神秘莫测,你不确信他将怎么行动,那么你可以说:
My brother is a wild card when it comes to holidays – we never know if he is coming to our family dinners.
最后,在计算机的世界里,wild card描述通配符。它看起来是个星号。如:
For example, if you want a computer to find all the words in a document that start with the prefix dis- so you can write a command: "Find dis*." The result will be a set of words like these: discover, discuss, dismiss, and the like.
(原文供参考)
American friend usually says. It is the… expression "a wild card." What does it mean?
This expression is like many that come to American English from card games, especially the game of poker.
Each set of playing cards has two extra cards. These can take the place of lost or damaged cards. These cards are called "Jokers." They often have a picture of a clown-like person sometimes called a jester.
In some card games, the "Joker" card is dealt to the players along with the usual kinds of cards. It serves as a "wild card" in the game. That means, if you have it in your hand, you can use it as any other card. Here is an example of how a player might talk about their use of a wild card in a game.
I have three kings and one joker. I'll use the wild card as a king, so now I have four kings.
But card games are not the only places you will hear the term "wild card." In sports, we use the expression for a team that is invited to compete for a championship. For example, in American football, the best teams play each other at the end of the season – in a series of games called "playoffs." A few teams that might not have the best records are invited to play against the top teams. It is possible, if the wildcard team is good enough, that it can win the championship.
Here is an example showing how a wild card team won the championship of American football, the Super Bowl.
In 2007, the New York Giants entered the playoffs as a wild card team, but they went on to beat the New England Patriots and win the Super Bowl.
And, as you can probably guess, we use "wild card" to describe people as well. If a person is unpredictable, you cannot be sure of how they will act. Then you might say:
My brother is a wild card when it comes to holidays – we never know if he is coming to our family dinners.
Finally, in the world of computers, the term "wild card" describes a symbol that programmers use. It looks like a little star and is called an asterisk. It means anything can appear in that place. For example, if you want a computer to find all the words in a document that start with the prefix dis- so you can write a command: "Find dis*." The result will be a set of words like these: discover, discuss, dismiss, and the like.
3.《喋血复仇》官方罗列已知BUG 并称正在积极修复
《喋血复仇》在正式在全球发售后,玩家们就已经发现了各种奇怪的BUG。开发商Turtle Rock表示他们已经看到了这些问题,并发布了一份《喋血复仇》BUG列表,并称他们正努力解决这些BUG。
喋血复仇已知问题及近期修复目标:
修复了Doc, Hoffman, Jim和Karlee在单人战役中无法解锁的BUG。
单人战役:调查如何在单人战役中获得补给点数和成就。
匹配的稳定性。
不均匀的匹配。
退出比赛及重新连接会导致主武器消失。
在设置终极版皮肤为默认皮肤时,会回到登陆界面并且人物没有发生改变。
在章节完成后,用户名有时会出现数字。
在使用ADS时死亡将使玩家重生具有ADS缩放的效果。
在英特尔文件夹拾取器上没有显示“card here”
要做的事情(已确认并将在稍后讨论的问题)
卡片上的文字大小对大多数电视来说并不理想。
Rescue Pod中玩家的名字显示为bot的名字。
玩家的副武器在转移到游戏中的任意地图后会被卸载。
在游戏内成就列表中寻找秘密互动成就并不会被解锁,尽管在游戏内完成成就会被触发。注:成就不受影响,仍然有效。
快速进入比赛的玩家将携带:无铜、基本数量的弹药和基本武器。
玩家有时可以在最后快速进入最终地图。
4.NPE问题及解决办法
00、背景
现在DHub平台的微服务已经很多了,随着用户量访问的增加,异常日志也有所增长。
今天下午针对日志收集平台,大伙进行了集体讨论及模块任务认领,但谈及NullPointerException时,大家有些分歧。
空指针(Null Pointer Exception,NPE)是Java中最常见不过的异常,防止 NPE,是程序员的基本修养。
在Java所属的甲骨文官网里找到了一篇文章来。原文路径:https://www.oracle.com/technical-resources/articles/java/java8-optional.html
吴军曾经介绍过一个系统性学习的三段论方法:
1、阅读正统文献或者教材(基线思维)
2、权威的综述性文章
3、学术专著阅读。
01、使代码更具可读性,并防止出现空指针异常
A wise man once said you are not a real Java programmer until you've dealt with a null pointer exception. Joking aside, the null reference is the source of many problems because it is often used to denote the absence of a value. Java SE 8 introduces a new class called java.util.Optional that can alleviate some of these problems.
Let's start with an example to see the dangers of null. Let's consider a nested object structure for a Computer, as illustrated in Figure 1.
What's possibly problematic with the following code?
String version = computer.getSoundcard().getUSB().getVersion();
This code looks pretty reasonable. However, many computers (for example, the Raspberry Pi) don't actually ship with a sound card. So what is the result of getSoundcard()?
A common (bad) practice is to return the null reference to indicate the absence of a sound card. Unfortunately, this means the call to getUSB() will try to return the USB port of a null reference, which will result in a NullPointerException at runtime and stop your program from running further. Imagine if your program was running on a customer's machine; what would your customer say if the program suddenly failed?
To give some historical context, Tony Hoare—one of the giants of computer science—wrote, "I call it my billion-dollar mistake. It was the invention of the null reference in 1965. I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement."
What can you do to prevent unintended null pointer exceptions? You can be defensive and add checks to prevent null dereferences, as shown in Listing 1:丑陋代码
String version = "UNKNOWN";
if(computer != null){
Soundcard soundcard = computer.getSoundcard();
if(soundcard != null){
USB usb = soundcard.getUSB();
if(usb != null){
version = usb.getVersion();
}
}
}
However, you can see that the code in Listing 1 quickly becomes very ugly due to the nested checks. Unfortunately, we need a lot of boilerplate code to make sure we don't get a NullPointerException. In addition, it's just annoying that these checks get in the way of the business logic. In fact, they are decreasing the overall readability of our program.
Furthermore, it is an error-prone process; what if you forget to check that one property could be null? I will argue in this article that using null to represent the absence of a value is a wrong approach. What we need is a better way to model the absence and presence of a value.
To give some context, let's briefly look at what other programming languages have to offer.
02、有什么替代 Null 的方法?
Languages such as Groovy have a safe navigation operator represented by "?." to safely navigate through potential null references. (Note that it is soon to be included in C#, too, and it was proposed for Java SE 7 but didn't make it into that release.) It works as follows:
String version = computer?.getSoundcard()?.getUSB()?.getVersion();
In this case, the variable version will be assigned to null if computer is null, or getSoundcard() returns null, or getUSB() returns null. You don't need to write complex nested conditions to check for null.
In addition, Groovy also includes the Elvis operator "?:" (if you look at it sideways, you'll recognize Elvis' famous hair), which can be used for simple cases when a default value is needed. In the following, if the expression that uses the safe navigation operator returns null, the default value "UNKNOWN" is returned; otherwise, the available version tag is returned.
String version =
computer?.getSoundcard()?.getUSB()?.getVersion() ?: "UNKNOWN";
Other functional languages, such as Haskell and Scala, take a different view. Haskell includes a Maybe type, which essentially encapsulates an optional value. A value of type Maybe can contain either a value of a given type or nothing. There is no concept of a null reference. Scala has a similar construct called Option[T] to encapsulate the presence or absence of a value of type T. You then have to explicitly check whether a value is present or not using operations available on the Option type, which enforces the idea of "null checking." You can no longer "forget to do it" because it is enforced by the type system.
OK, we perged a bit and all this sounds fairly abstract. You might now wonder, "so, what about Java SE 8?"
03、Optional 简述
Java SE 8 introduces a new class called java.util.Optional<T> that is inspired from the ideas of Haskell and Scala. It is a class that encapsulates an optional value, as illustrated in Listing 2 below and in Figure 1. You can view Optional as a single-value container that either contains a value or doesn't (it is then said to be "empty"), as illustrated in Figure 2.
We can update our model to make use of Optional, as shown in Listing 2:
public class Computer {
private Optional<Soundcard> soundcard;
public Optional<Soundcard> getSoundcard() { ... }
...
}
public class Soundcard {
private Optional<USB> usb;
public Optional<USB> getUSB() { ... }
}
public class USB{
public String getVersion(){ ... }
}
The code in Listing 2 immediately shows that a computer might or might not have a sound card (the sound card is optional). In addition, a sound card can optionally have a USB port. This is an improvement, because this new model can now reflect clearly whether a given value is allowed to be missing. Note that similar ideas have been available in libraries such as Guava.
But what can you actually do with an Optional<Soundcard> object? After all, you want to get to the USB port's version number. In a nutshell, the Optional class includes methods to explicitly deal with the cases where a value is present or absent. However, the advantage compared to null references is that the Optional class forces you to think about the case when the value is not present. As a consequence, you can prevent unintended null pointer exceptions.
It is important to note that the intention of the Optional class is not to replace every single null reference. Instead, its purpose is to help design more-comprehensible APIs so that by just reading the signature of a method, you can tell whether you can expect an optional value. This forces you to actively unwrap an Optional to deal with the absence of a value.
04、采用 Optional 的模式
Enough talking; let's see some code! We will first explore how typical null-check patterns can be rewritten using Optional. By the end of this article, you will understand how to use Optional, as shown below, to rewrite the code in Listing 1 that was doing several nested null checks:
String name = computer.flatMap(Computer::getSoundcard)
.flatMap(Soundcard::getUSB)
.map(USB::getVersion)
.orElse("UNKNOWN");
Note: Make sure you brush up on the Java SE 8 lambdas and method references syntax (see "Java 8: Lambdas[1]") as well as its stream pipelining concepts (see "Processing Data with Java SE 8 Streams").
05、创建 Optional 对象
First, how do you create Optional objects? There are several ways:
Here is an empty Optional:
Optional<Soundcard> sc = Optional.empty();
And here is an Optional with a non-null value:
SoundCard soundcard = new Soundcard();
Optional<Soundcard> sc = Optional.of(soundcard);
If soundcard were null, a NullPointerException would be immediately thrown (rather than getting a latent error once you try to access properties of the soundcard).
Also, by using ofNullable, you can create an Optional object that may hold a null value:
Optional<Soundcard> sc = Optional.ofNullable(soundcard);
If soundcard were null, the resulting Optional object would be empty.
06、如果存在值,则执行某些操作
Now that you have an Optional object, you can access the methods available to explicitly deal with the presence or absence of values. Instead of having to remember to do a null check, as follows:
SoundCard soundcard = ...;
if(soundcard != null){
System.out.println(soundcard);
}
You can use the ifPresent() method, as follows:
Optional<Soundcard> soundcard = ...;
soundcard.ifPresent(System.out::println);
You no longer need to do an explicit null check; it is enforced by the type system. If the Optional object were empty, nothing would be printed.
You can also use the isPresent() method to find out whether a value is present in an Optional object. In addition, there's a get() method that returns the value contained in the Optional object, if it is present. Otherwise, it throws a NoSuchElementException. The two methods can be combined, as follows, to prevent exceptions:
if(soundcard.isPresent()){
System.out.println(soundcard.get());
}
However, this is not the recommended use of Optional (it's not much of an improvement over nested null checks), and there are more idiomatic alternatives, which we explore below.
07、默认值和操作
A typical pattern is to return a default value if you determine that the result of an operation is null. In general, you can use the ternary operator, as follows, to achieve this:
Soundcard soundcard =
maybeSoundcard != null ? maybeSoundcard
: new Soundcard("basic_sound_card");
Using an Optional object, you can rewrite this code by using the orElse() method, which provides a default value if Optional is empty:
Soundcard soundcard = maybeSoundcard.orElse(new Soundcard("defaut"));
Similarly, you can use the orElseThrow() method, which instead of providing a default value if Optional is empty, throws an exception:
Soundcard soundcard =
maybeSoundCard.orElseThrow(IllegalStateException::new);
08、使用 filter 方法拒绝某些值
Often you need to call a method on an object and check some property. For example, you might need to check whether the USB port is a particular version. To do this in a safe way, you first need to check whether the reference pointing to a USB object is null and then call the getVersion() method, as follows:
USB usb = ...;
if(usb != null && "3.0".equals(usb.getVersion())){
System.out.println("ok");
}
This pattern can be rewritten using the filter method on an Optional object, as follows:
Optional<USB> maybeUSB = ...;
maybeUSB.filter(usb -> "3.0".equals(usb.getVersion())
.ifPresent(() -> System.out.println("ok"));
The filter method takes a predicate as an argument. If a value is present in the Optional object and it matches the predicate, the filter method returns that value; otherwise, it returns an empty Optional object. You might have seen a similar pattern already if you have used the filter method with the Stream interface.
09、使用 map 方法提取和转换值
Another common pattern is to extract information from an object. For example, from a Soundcard object, you might want to extract the USB object and then further check whether it is of the correct version. You would typically write the following code:
if(soundcard != null){
USB usb = soundcard.getUSB();
if(usb != null && "3.0".equals(usb.getVersion()){
System.out.println("ok");
}
}
We can rewrite this pattern of "checking for null and extracting" (here, the Soundcard object) using the map method.
Optional<USB> usb = maybeSoundcard.map(Soundcard::getUSB);
There's a direct parallel to the map method used with streams. There, you pass a function to the map method, which applies this function to each element of a stream. However, nothing happens if the stream is empty.
The map method of the Optional class does exactly the same: the value contained inside Optional is "transformed" by the function passed as an argument (here, a method reference to extract the USB port), while nothing happens if Optional is empty.
Finally, we can combine the map method with the filter method to reject a USB port whose version is different than 3.0:
maybeSoundcard.map(Soundcard::getUSB)
.filter(usb -> "3.0".equals(usb.getVersion())
.ifPresent(() -> System.out.println("ok"));
Awesome; our code is starting to look closer to the problem statement and there are no verbose null checks getting in our way!
10、级联Optional对象使用 flatMap 方法
You've seen a few patterns that can be refactored to use Optional. So how can we write the following code in a safe way?
String version = computer.getSoundcard().getUSB().getVersion();
Notice that all this code does is extract one object from another one, which is exactly what the map method is for. Earlier in the article, we changed our model so a Computer has an Optional<Soundcard> and a Soundcard has an Optional<USB>, so we should be able to write the following:
String version = computer.map(Computer::getSoundcard)
.map(Soundcard::getUSB)
.map(USB::getVersion)
.orElse("UNKNOWN");
Unfortunately, this code doesn't compile. Why? The variable computer is of type Optional<Computer>, so it is perfectly correct to call the map method. However, getSoundcard() returns an object of type Optional<Soundcard>. This means the result of the map operation is an object of type Optional<Optional<Soundcard>>. As a result, the call to getUSB() is invalid because the outermost Optional contains as its value another Optional, which of course doesn't support the getUSB() method. Figure 3 illustrates the nested Optional structure you would get.
So how can we solve this problem? Again, we can look at a pattern you might have used previously with streams: the flatMap method. With streams, the flatMap method takes a function as an argument, which returns another stream. This function is applied to each element of a stream, which would result in a stream of streams. However, flatMap has the effect of replacing each generated stream by the contents of that stream. In other words, all the separate streams that are generated by the function get amalgamated or "flattened" into one single stream. What we want here is something similar, but we want to "flatten" a two-level Optional into one.
Well, here's good news: Optional also supports a flatMap method. Its purpose is to apply the transformation function on the value of an Optional (just like the map operation does) and then flatten the resulting two-level Optional into a single one. Figure 4 illustrates the difference between map and flatMap when the transformation function returns an Optional object.
So, to make our code correct, we need to rewrite it as follows using flatMap:
String version = computer.flatMap(Computer::getSoundcard)
.flatMap(Soundcard::getUSB)
.map(USB::getVersion)
.orElse("UNKNOWN");
The first flatMap ensures that an Optional<Soundcard> is returned instead of an Optional<Optional<Soundcard>>, and the second flatMap achieves the same purpose to return an Optional<USB>. Note that the third call just needs to be a map() because getVersion() returns a String rather than an Optional object.
Wow! We've come a long way from writing painful nested null checks to writing declarative code that is composable, readable, and better protected from null pointer exceptions.