通配符和上下界
需求:
- 有水果类,有苹果类,苹果继承水果,有一个装水果的盘子。
- 实现向盘子里放入水果,实现从盘子里取出水果。
- 实现向盘子里放入苹果,实现从盘子里取出苹果。
plate相当于一个容器,水果类与苹果类是父子关系可以适用里氏替换原则。但是用plate装起来的水果类与苹果类就不再适用。就算容器里装的东西之间有继承关系,但容器之间是没有继承关系的。
代码示例:
//父类:水果
class Fruit{
}
//子类:苹果
class Apple extends Fruit{
}
//定义一个容器,放水果的盘子,设计为泛型,所以可以放各种水果。通过get和set方法提供 “放” 和 “取” 的动作)
class Plate<T>{
private T item;
public Plate(T item){
this.item = item;
}
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}
//测试
public class Test {
public static void main(String[] args) {
//定义一个水果盘子,放水果
Plate<Fruit> p1 = new Plate<Fruit>(new Fruit());//正确
//定义一个苹果盘子,放苹果
Plate<Apple> p2 = new Plate<Apple>(new Apple());//正确
//定义一个水果盘子,放苹果
Plate<Fruit> p3 = new Plate<Apple>(new Apple());//错误
//定义一个苹果盘子,放水果
Plate<Apple> p4 = new Plate<Fruit>(new Apple());//错误
}
}
程序第9和第11行报编译错误。为什么?
逻辑上,水果盘子当然可以放苹果,因为苹果是水果的子类,里氏替换原则是允许用子类对象代替父类对象的。
但实际上Java编译器不允许这个操作,
第 9行会报出 “ 装苹果的盘子无法转换为装水果的盘子 ”
第11行会报出 “ 装水果的盘子无法转换为装苹果的盘子 ”
实际上,编译器的逻辑是这样的
- 苹果 is a 水果
- 装苹果的盘子 not is a 装水果的盘子
所以,就算容器里装的东西之间有继承关系,但容器之间是没有继承关系的。
解决办法是使用上界通配符和下届通配符
什么是上界通配符?
把父类看作是继承关系中的上界。
上界通配符
Plate<? extend 父类>
表示限定了这个盘子可以放的东西上界,所以上界和上界以下的东西可以放进去,即父类和子类都能放进去。
修改第9行的代码为
Plate<? extends Fruit> p3 = new Plate<Apple>(new Apple());//正确
什么是下界通配符?
把子类看作是继承关系中的下界。
下界通配符
Plate<? super 子类>
表示限定了这个盘子可以放的东西下界,所以下界和下界以上的东西可以放进去,即子类和父类都能放进去。
修改第11行的代码为
Plate<? super Apple> p4 = new Plate<Fruit>(new Fruit());//正确
Lambda的限制条件
Lambda表达式在实现接口时,接口中只允许有且只有一个抽象方法。
这种限制可以使用函数式接口来定义
String类中涉及正则表达式的方法
String类中提供了matches()方法用于验证正则表达式,另外split()方法和replaceAll()方法中也可以使用正则表达式。
- boolean matches(String regex)
- String [] split(String regex)
- String replaceAll(String regex,String replacement)
public boolean matches(String regex) :判断字符串是否匹配给定的规则
/*
* 举例:校验qq号码
* 1: 要求必须是5-15位数字
* 2: 0不能开头
*/
String qq = "12882274";
String regex = "[1-9][0-9]{4,14}";
boolean result = qq.matches(regex);
System.out.println(result);
public boolean matches(String regex) :判断字符串是否匹配给定的规则
/*
* 举例:校验手机号码
* 1:要求为11位数字
* 2:第1位为1,第2位为3、4、5、7、8中的一个,后面9位为0到9之间的任意数字
*/
String phone = "13579246810";
String regex = "1[34578][0-9]{9}";
boolean result = phone.matches(regex);
System.out.println(result);
public String[] split(String regex) 根据给定正则表达式的匹配规则,拆分此字符串
/*
* 举例:分割出字符串中的的数字
*/
String s = "1-20-300-4000";
String regex = "-";
String[] result = s.split(regex);//根据给定正则表达式的匹配规则,拆分此字符串
System.out.println(Arrays.toString(result));//[1, 20, 300, 4000]
String s1 = "1/20/300/4000-50000-6000000";//根据给定正则表达式的匹配规则,拆分此字符串
String regex1 = "[/-]";//根据 / 或 - 拆分字符串
String[] result1 = s1.split(regex1);
System.out.println(Arrays.toString(result1));//[1, 20, 300, 4000, 50000, 6000000]
public String replaceAll(String regex,String replacement):将符合规则的字符串内容,全部替换为新字符串
/*
* 举例:把文字中的数字替换成*
*/
String s = "Hello01234World56789";
String regex = "[0-9]";
String result = s.replaceAll(regex, "*");
System.out.println(result);//Hello*****World*****
hashCode()
猜猜这段代码输出什么
class person{
public int age;
public int num;
public person(int age, int num) {
this.age = age;
this.num = num;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
public class hashCodeDetail {
public static void main(String[] args) {
person p1 = new person(1,2);
HashSet<Object> set = new HashSet<>();
set.add(p1);
p1.setAge(2);
p1.setNum(1);
set.add(p1);
System.out.println(set.size());
}
}
输出 1 因为默认hashCode方法是根据对象的地址值,而不是内容,如果要判断内容需要重写hashCode与equals方法
class person{
public int age;
public int num;
public person(int age, int num) {
this.age = age;
this.num = num;
}
public void setAge(int age) {
this.age = age;
}
public void setNum(int num) {
this.num = num;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
person person = (person) o;
return age == person.age && num == person.num;
}
@Override
public int hashCode() {
return Objects.hash(age, num);
}
}
public class hashCodeDetail {
public static void main(String[] args) {
person p1 = new person(1,2);
HashSet<Object> set = new HashSet<>();
set.add(p1);
p1.setAge(2);
p1.setNum(1);
set.add(p1);
System.out.println(set.size());
}
}
此时这段代码输出的是2 重写后都是比较值的方法
点进objects这个工具类的hash方法可以看出他是根据内容改变hash值的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n4tkZ1cS-1659938698974)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20220805145651059.png)]
bject> set = new HashSet<>();
set.add(p1);
p1.setAge(2);
p1.setNum(1);
set.add(p1);
System.out.println(set.size());
}
}
此时这段代码输出的是2 重写后都是比较值的方法
点进objects这个工具类的hash方法可以看出他是根据内容改变hash值的
![在这里插入图片描述](https://img-blog.csdnimg.cn/959d4237da65492194145bdec5855240.png#pic_center)