Java中setters和getters的意义是什么?

2022-09-01 10:11:17

请原谅长度,但这里有两个程序,它们都完全相同,但一个带有和一个没有 setter、getter 和构造函数。

我以前上过一堂基本的C++课,不记得其中的任何一个,目前我还没有看到它们的意义,如果有人可以用拉门的术语解释它们,我将不胜感激......目前,它们似乎只不过是浪费空间,以使我的代码看起来更长,但老师说它们很重要(到目前为止就是这样)。

提前致谢!现在代码如下:里程.java:

package gasMileage;

import java.util.Scanner; //program uses class Scanner

public class Mileage 
{
    public int restart;
    public double miles, gallons, totalMiles, totalGallons, milesPerGallon;
    public Mileage(int newRestart, double newMiles, double newGallons, 
                   double newTotalMiles, double newTotalGallons, double newMilesPerGallon)
    {
        setRestart(newRestart);
        setMiles(newMiles);
        setGallons(newGallons);
        setTotalMiles(newTotalMiles);
        setTotalGallons(newTotalGallons);
        setMilesPerGallon(newMilesPerGallon);
    }
    public void setRestart(int newRestart)
    {
        restart = newRestart;
    }
    public int getRestart()
    {
        return restart;
    }
    public void setMiles(double newMiles)
    {
        miles = newMiles;
    }
    public double getMiles()
    {
        return miles;
    }
    public void setGallons(double newGallons)
    {
        gallons = newGallons;
    }
    public double getGallons()
    {
        return gallons;
    }
    public void setTotalMiles(double newTotalMiles)
    {
        totalMiles = newTotalMiles;
    }
    public double getTotalMiles()
    {
        return totalMiles;
    }
    public void setTotalGallons(double newTotalGallons)
    {
        totalGallons = newTotalGallons;
    }
    public double getTotalGallons()
    {
        return totalGallons;
    }
    public void setMilesPerGallon(double newMilesPerGallon)
    {
        milesPerGallon = newMilesPerGallon;
    }
    public double getMilesPerGallon()
    {
        return milesPerGallon;
    }
    public void calculateMileage()
    {
        Scanner input = new Scanner(System.in);
        while(restart == 1)
        {
            System.out.print("Please input number of miles you drove: ");
            miles = input.nextDouble();
            totalMiles = totalMiles + miles;
            System.out.print("Please input number of gallons you used: ");
            gallons = input.nextDouble();
            totalGallons = totalGallons + gallons;
            milesPerGallon = miles / gallons;
            System.out.printf("Your mileage is %.2f MPG.\n", milesPerGallon);
            System.out.print("Would you like to try again? 1 for yes, 2 for no: ");
            restart = input.nextInt();
        }
        milesPerGallon = totalMiles / totalGallons;
        System.out.printf("Your total mileage for these trips is: %.2f.\nYour total gas consumed on these trips was: %.2f.\n", totalMiles, totalGallons);
        System.out.printf("Your total mileage for these trips is: %.2f MPG", milesPerGallon);
    }
}

里程测试.java:

package gasMileage;

public class Mileagetest 
{
    public static void main(String[] args) 
    {
        Mileage myMileage = new Mileage(1,0,0,0,0,0);
        myMileage.calculateMileage();
    }
}

现在对于没有二传手和获取器的那个:

测试里程.java:

package gasMileage;

import java.util.Scanner;

public class Testmileage 
{
    int restart = 1;
    double miles = 0, milesTotal = 0, gas = 0, gasTotal = 0, mpg = 0;
    Scanner input = new Scanner(System.in);
    public void testCalculate()
    {
        while(restart == 1)
        {
            System.out.print("Please input miles: ");
            miles = input.nextDouble();
            milesTotal = milesTotal + miles;
            System.out.print("Please input gas: ");
            gas = input.nextDouble();
            gasTotal = gasTotal + gas;
            mpg = miles/gas;
            System.out.printf("MPG: %.2f", mpg);
            System.out.print("\nContinue? 1 = yes, 2 = no: ");
            restart = input.nextInt();
        }
            mpg = milesTotal / gasTotal;
            System.out.printf("Total Miles: %.2f\nTotal Gallons: %.2f\nTotal MPG: %.2f\n", milesTotal, gasTotal, mpg);
    }
}

Testmileagetest.java:

package gasMileage;

public class Testmileagetest 
{

    /**
     * @param args
     */
    public static void main(String[] args) 
    {
        Testmileage test = new Testmileage();
        test.testCalculate();
    }

}

再次感谢!


答案 1

无论语言如何,getter 和 setter 的要点是隐藏底层变量。这允许您在尝试设置值时添加验证逻辑 - 例如,如果您有出生日期字段,则可能只想允许将该字段设置为过去的某个时间。如果字段可公开访问和可修改,则无法强制执行 - 您需要 getter 和 setter。

即使您还不需要任何验证,将来也可能需要它。现在编写 getter 和 setter 意味着接口保持一致,因此现有代码在更改时不会中断。


答案 2

封装

访问器方法(“setters 和 getters”)尝试隐藏有关如何存储对象中的数据的详细信息。在实践中,它们是以非面向对象的方式存储和检索数据的美化方法。访问器不能有效地封装任何内容,因为以下两段代码之间几乎没有实际区别:

Person bob = new Person();
Colour hair = bob.getHairColour();
hair.setRed( 255 );

还有这个:

Person bob = new Person();
Colour hair = bob.hairColour;
hair.red = 255;

这两个代码片段都揭示了一个人与头发紧密耦合的想法。然后,这种紧密耦合在整个代码库中显现出来,从而导致软件脆弱。也就是说,很难改变一个人的头发的储存方式。

相反:

Person bob = new Person();
bob.colourHair( Colour.RED );

这遵循了“告诉,不要问”的前提。换句话说,应该指示对象(由其他对象)执行特定任务。这就是面向对象编程的全部意义所在。而且似乎很少有人明白这一点。

这两种方案之间的区别在于:

  • 在第一种情况下,鲍勃无法控制他的头发会变成什么颜色。非常适合喜欢红头发的发型师,对于鄙视这种颜色的Bob来说就不那么好了。
  • 在第二种情况下,Bob 可以完全控制他的头发将变成什么颜色,因为未经 Bob 的许可,系统中的其他对象都不允许更改该颜色。

避免此问题的另一种方法是返回 Bob 的头发颜色的副本(作为新实例),该副本不再与 Bob 耦合。我发现这是一个不优雅的解决方案,因为这意味着另一个阶级想要的行为,使用一个人的头发,不再与这个人本身相关联。这降低了重用代码的能力,从而导致重复代码。

隐藏数据类型

在 Java 中,不能有两个仅因返回类型而不同的方法签名,它实际上不会隐藏对象使用的基础数据类型。您很少(如果有的话)会看到以下内容:

public class Person {
  private long hColour = 1024;

  public Colour getHairColour() {
    return new Colour( hColour & 255, hColour << 8 & 255, hColour << 16 & 255 );
  }
}

通常,各个变量通过使用相应的访问器逐字公开其数据类型,并且需要重构才能更改它:

public class Person {
  private long hColour = 1024;

  public long getHairColour() {
    return hColour;
  }

  /** Cannot exist in Java: compile error. */
  public Colour getHairColour() {
    return new Colour( hColour & 255, hColour << 8 & 255, hColour<< 16 & 255 );
  }
}

虽然它提供了一定程度的抽象,但它是一个薄薄的面纱,对松散耦合没有任何作用。

告诉,不要问

有关此方法的详细信息,请阅读告诉,不要问

文件示例

考虑以下代码,从ColinD的答案中略微修改:

public class File {
   private String type = "";

   public String getType() {
      return this.type;
   }

   public void setType( String type ) {
      if( type = null ) {
        type = "";
      }

      this.type = type;
   }

   public boolean isValidType( String type ) {
      return getType().equalsIgnoreCase( type );
   }
}

在这种情况下,该方法是多余的,并且将不可避免地(在实践中)导致重复的代码,例如:getType()

public void arbitraryMethod( File file ) {
  if( file.getType() == "JPEG" ) {
    // Code.
  }
}

public void anotherArbitraryMethod( File file ) {
  if( file.getType() == "WP" ) {
    // Code.
  }
}

问题:

  • 数据类型。该属性不能轻易地从 String 更改为整数(或其他类)。type
  • 隐含协议。将类型从特定 (, , , ) 抽象为一般 (, , ) 非常耗时。PNGJPEGTIFFEPSIMAGEDOCUMENTSPREADSHEET
  • 介绍错误。更改隐含的协议不会生成编译器错误,这可能会导致错误。

通过阻止其他类请求数据来完全避免此问题:

public void arbitraryMethod( File file ) {
  if( file.isValidType( "JPEG" ) ) {
    // Code.
  }
}

这意味着将访问器方法更改为 :getprivate

public class File {
   public final static String TYPE_IMAGE = "IMAGE";

   private String type = "";

   private String getType() {
      return this.type;
   }

   public void setType( String type ) {
      if( type == null ) {
        type = "";
      }
      else if(
        type.equalsIgnoreCase( "JPEG" ) ||
        type.equalsIgnoreCase( "JPG" ) ||
        type.equalsIgnoreCase( "PNG" ) ) {
        type = File.TYPE_IMAGE;
      }

      this.type = type;
   }

   public boolean isValidType( String type ) {
      // Coerce the given type to a generic type.
      //
      File f = new File( this );
      f.setType( type );

      // Check if the generic type is valid.
      //
      return isValidGenericType( f.getType() );
   }
}

当类将隐含的协议从特定类型(例如JPEG)转换为通用类型(例如,IMAGE)时,系统中没有其他代码会中断。系统中的所有代码都必须使用该方法,该方法不将类型提供给调用对象,而是告诉类验证类型。FileisValidTypeFile