Skip to content

Commit 6492883

Browse files
committed
完成SafeVarargs注解的说明
1 parent bc2443e commit 6492883

7 files changed

Lines changed: 4735 additions & 10 deletions

File tree

advance/annotation.lyx

Lines changed: 290 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -441,16 +441,28 @@ deprecation 忽略调用过时方法的警告。
441441
\end_layout
442442

443443
\begin_layout Standard
444-
@SafeVarargs在JDK 7中引入,主要目的是处理可变长参数中的泛型,此注解告诉编译器:在可变长参数中的泛型是类型安全的。可变长参数是使用数组存储的,而
445-
数组和泛型不能很好的混合使用
444+
@SafeVarargs在JDK 7中引入,主要目的是处理可变长参数中的泛型,此注解告诉编译器:在可变长参数中的泛型是类型安全的,不需要(请忽略)给出警告信息。
445+
可变长参数是使用数组存储的,而数组和泛型不能很好的混合使用
446446
\begin_inset CommandInset citation
447447
LatexCommand cite
448448
after "p105,第25条:列表优先于数组"
449449
key "effective-java"
450450

451451
\end_inset
452452

453-
,因此当在可变长参数中使用泛型时,编译器会给出警告信息。考虑
453+
454+
\begin_inset Foot
455+
status open
456+
457+
\begin_layout Plain Layout
458+
简单的说,数组元素的数据类型在编译和运行时都是确定的,而泛型的数据类型只有在运行时才能确定下来,因此当把一个泛型存储到数组中时,编译器在编译阶段无法检查数据类型
459+
是否匹配,因此会给出警告信息:存在可能的“堆污染”(heap pollution),即如果泛型的真实数据类型无法和参数数组的类型匹配,会导致ClassCastE
460+
xception异常。
461+
\end_layout
462+
463+
\end_inset
464+
465+
,因此当在可变长参数中使用泛型时,编译器都会给出警告信息。考虑
454466
\begin_inset ERT
455467
status open
456468

@@ -471,6 +483,14 @@ reference "SafeVarargsTest.java"
471483
\end_inset
472484

473485

486+
\begin_inset CommandInset citation
487+
LatexCommand cite
488+
after "p35"
489+
key "java-7-new-feature-cookbook"
490+
491+
\end_inset
492+
493+
474494
\end_layout
475495

476496
\begin_layout Standard
@@ -588,8 +608,8 @@ but why?
588608

589609
\end_inset
590610

591-
。一个方法使用@SafeVarargs注解的前提是,开发人员必须确保这个方法的实现中对泛型类型参数的处理不会引发类型安全问题,否则可能导致运行时的类型转换异常
592-
比如
611+
。一个方法使用@SafeVarargs注解的前提是,开发人员必须确保这个方法的实现中对泛型类型参数的处理不会引发类型安全问题,否则可能导致运行时的类型转换异常
612+
下面给出一个“堆污染”的实例,参见
593613
\begin_inset ERT
594614
status open
595615

@@ -616,7 +636,271 @@ reference "UnsafeMethodTest.java"
616636
\begin_inset CommandInset include
617637
LatexCommand lstinputlisting
618638
filename "code/annotation/src/cn/edu/sdut/softlab/safevarargs/UnsafeMethodTest.java"
619-
lstparams "caption={并不安全的@SafeVarargs:UnsafeMethodTest.java},label={UnsafeMethodTest.java}"
639+
lstparams "numbers=left,caption={并不安全的@SafeVarargs:UnsafeMethodTest.java},label={UnsafeMethodTest.java}"
640+
641+
\end_inset
642+
643+
644+
\end_layout
645+
646+
\begin_layout Standard
647+
运行UnsafeMethodTest的结果如下:
648+
\end_layout
649+
650+
\begin_layout Standard
651+
\begin_inset Box Shaded
652+
position "t"
653+
hor_pos "c"
654+
has_inner_box 1
655+
inner_pos "t"
656+
use_parbox 0
657+
use_makebox 0
658+
width "100col%"
659+
special "none"
660+
height "1in"
661+
height_special "totalheight"
662+
thickness "0.4pt"
663+
separation "3pt"
664+
shadowsize "4pt"
665+
framecolor "black"
666+
backgroundcolor "none"
667+
status collapsed
668+
669+
\begin_layout Plain Layout
670+
Exception in thread "main" java.lang.
671+
\begin_inset Flex Strong
672+
status open
673+
674+
\begin_layout Plain Layout
675+
ClassCastException: java.lang.Integer cannot be cast to java.lang.String
676+
\end_layout
677+
678+
\end_inset
679+
680+
681+
\end_layout
682+
683+
\begin_layout Plain Layout
684+
at cn.edu.sdut.softlab.safevarargs.UnsafeMethodTest.unsafeMethod( UnsafeMethodTest.java
685+
:22 )
686+
\end_layout
687+
688+
\begin_layout Plain Layout
689+
at cn.edu.sdut.softlab.safevarargs.UnsafeMethodTest.main( UnsafeMethodTest.java:14
690+
)
691+
\end_layout
692+
693+
\end_inset
694+
695+
696+
\end_layout
697+
698+
\begin_layout Standard
699+
对这个结果意外吗?我们来详细分析一下。在
700+
\begin_inset ERT
701+
status open
702+
703+
\begin_layout Plain Layout
704+
705+
706+
\backslash
707+
lstlistingname
708+
\end_layout
709+
710+
\end_inset
711+
712+
713+
\begin_inset CommandInset ref
714+
LatexCommand ref
715+
reference "UnsafeMethodTest.java"
716+
717+
\end_inset
718+
719+
中,当代码执行到第22行时的状态如
720+
\begin_inset ERT
721+
status open
722+
723+
\begin_layout Plain Layout
724+
725+
726+
\backslash
727+
figurename
728+
\end_layout
729+
730+
\end_inset
731+
732+
733+
\begin_inset CommandInset ref
734+
LatexCommand ref
735+
reference "fig:可变长参数的初始状态"
736+
737+
\end_inset
738+
739+
所示,数组array和stringLists同时指向了参数数组,tmpList是一个包含两个Integer对象的list对象。
740+
\end_layout
741+
742+
\begin_layout Standard
743+
\begin_inset Float figure
744+
wide false
745+
sideways false
746+
status collapsed
747+
748+
\begin_layout Plain Layout
749+
\align center
750+
\begin_inset Graphics
751+
filename imgs/annotation/safevarargs-1.eps
752+
width 90line%
753+
754+
\end_inset
755+
756+
757+
\end_layout
758+
759+
\begin_layout Plain Layout
760+
\begin_inset Caption Standard
761+
762+
\begin_layout Plain Layout
763+
可变长参数的初始状态
764+
\begin_inset CommandInset label
765+
LatexCommand label
766+
name "fig:可变长参数的初始状态"
767+
768+
\end_inset
769+
770+
771+
\end_layout
772+
773+
\end_inset
774+
775+
776+
\end_layout
777+
778+
\begin_layout Plain Layout
779+
780+
\end_layout
781+
782+
\end_inset
783+
784+
785+
\end_layout
786+
787+
\begin_layout Standard
788+
当程序执行到
789+
\begin_inset Foot
790+
status open
791+
792+
\begin_layout Plain Layout
793+
这里的赋值操作是合法的,因为tmplist是List类型的对象,array是一个Object类型的数组,根据Java允许“向上塑型”的原则,array数组能够接
794+
受任意类型的对象。
795+
\end_layout
796+
797+
\end_inset
798+
799+
800+
\end_layout
801+
802+
\begin_layout Standard
803+
\begin_inset listings
804+
inline false
805+
status open
806+
807+
\begin_layout Plain Layout
808+
809+
array[0] = tmpList;
810+
\end_layout
811+
812+
\end_inset
813+
814+
815+
\end_layout
816+
817+
\begin_layout Standard
818+
时,几个变量的关系如
819+
\begin_inset ERT
820+
status open
821+
822+
\begin_layout Plain Layout
823+
824+
825+
\backslash
826+
figurename
827+
\end_layout
828+
829+
\end_inset
830+
831+
832+
\begin_inset CommandInset ref
833+
LatexCommand ref
834+
reference "fig:可变长参数遭到堆污染"
835+
836+
\end_inset
837+
838+
所示,虚线表示原先的指向关系,实线表示新的指向关系。此时,参数数组的第0个元素指向了包含两个Integer对象的list对象tmpList。当进一步执行:
839+
\end_layout
840+
841+
\begin_layout Standard
842+
\begin_inset listings
843+
inline false
844+
status open
845+
846+
\begin_layout Plain Layout
847+
848+
String s = stringLists[0].get(0);
849+
\end_layout
850+
851+
\end_inset
852+
853+
854+
\end_layout
855+
856+
\begin_layout Standard
857+
时,从参数数组中取出第0个元素为list对象(tmpList),再取出list对象的第0个元素为Integer类型的对象(其值为42)。问题在这里出现了,我们试
858+
图将一个Integer类型的对象赋值给String类型的对象,显然会导致类型转换异常(ClassCastException)。
859+
\end_layout
860+
861+
\begin_layout Standard
862+
因此,这个方法是不应该标记为@SafeVarargs的。
863+
\end_layout
864+
865+
\begin_layout Standard
866+
\begin_inset Float figure
867+
wide false
868+
sideways false
869+
status open
870+
871+
\begin_layout Plain Layout
872+
\align center
873+
\begin_inset Graphics
874+
filename imgs/annotation/safevarargs-2.eps
875+
width 90line%
876+
877+
\end_inset
878+
879+
880+
\end_layout
881+
882+
\begin_layout Plain Layout
883+
\begin_inset Caption Standard
884+
885+
\begin_layout Plain Layout
886+
可变长参数遭到堆污染
887+
\begin_inset CommandInset label
888+
LatexCommand label
889+
name "fig:可变长参数遭到堆污染"
890+
891+
\end_inset
892+
893+
894+
\end_layout
895+
896+
\end_inset
897+
898+
899+
\end_layout
900+
901+
\begin_layout Plain Layout
902+
903+
\end_layout
620904

621905
\end_inset
622906

advance/code/annotation/src/cn/edu/sdut/softlab/safevarargs/UnsafeMethodTest.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@
1010
public class UnsafeMethodTest {
1111

1212
public static void main(String[] args) {
13-
List<String> str = Arrays.asList("abc", "def");
14-
unsafeMethod(str);
13+
List<String> list1 = Arrays.asList("one", "two");
14+
List<String> list2 = Arrays.asList("three","four");
15+
unsafeMethod(list1, list2);
1516
}
1617

1718
@SafeVarargs // 其实并不安全!
1819
static void unsafeMethod(List<String>... stringLists) {
1920
Object[] array = stringLists;
20-
List<Integer> tmpList = Arrays.asList(42);
21-
array[0] = tmpList; // 试图将一个List对象赋值给字符串,显然在语法上是错误的,但是由于使用了@SafeVarargs,编译器不做类型检查,能够编译通过
21+
List<Integer> tmpList = Arrays.asList(42, 56);
22+
array[0] = tmpList; // tmpList是一个List对象(类型已经擦除),赋值给Object类型的对象是允许的(向上塑型),能够编译通过
2223
String s = stringLists[0].get(0); // 运行时抛出ClassCastException!
2324
}
2425
}
2.3 KB
Binary file not shown.

0 commit comments

Comments
 (0)