Tại sao các phương thức tĩnh chỉ có thể sử dụng dữ liệu tĩnh?


38

Tôi không hiểu tại sao một phương thức tĩnh không thể sử dụng dữ liệu không tĩnh. Bất cứ ai có thể giải thích vấn đề là gì và tại sao chúng ta không thể làm điều đó?


11
Bởi vì chỉ có dữ liệu tĩnh tồn tại từ quan điểm của các phương thức tĩnh.
mouviciel

4
Chia sẻ nghiên cứu của bạn giúp mọi người. Hãy cho chúng tôi những gì bạn đã cố gắng và tại sao nó không đáp ứng nhu cầu của bạn. Điều này chứng tỏ rằng bạn đã dành thời gian để cố gắng tự giúp mình, nó giúp chúng ta tránh nhắc lại các câu trả lời rõ ràng và hầu hết nó giúp bạn có được câu trả lời cụ thể và phù hợp hơn. Xem thêm Cách hỏi
gnat

19
@gnat trong trường hợp này OP đang cố gắng hiểu lý do đằng sau một quyết định thiết kế. Bạn mong đợi anh ấy sẽ thử điều gì trong trường hợp này?
Geek

2
@Geek - sự tồn tại của các phương thức tĩnh, dữ liệu tĩnh là một vấn đề thiết kế ngôn ngữ. Giả sử ý nghĩa tiêu chuẩn, thực tế là các phương thức tĩnh không thể truy cập dữ liệu cá thể là không. Giới hạn được ngụ ý bởi các định nghĩa và những gì có thể và có ý nghĩa, không phải bởi một số nhà thiết kế ngôn ngữ.
Steve314

6
Để diễn giải Gertrude Stein: "Không có cái này ở đó."
hà mã-vũ công

Câu trả lời:


73

Trong hầu hết các ngôn ngữ OO, khi bạn định nghĩa một phương thức bên trong một lớp, nó sẽ trở thành một Phương thức sơ thẩm . Khi bạn tạo một mới dụ của lớp đó, thông qua các newtừ khóa, bạn khởi tạo một bộ mới của dữ liệu duy nhất để chỉ trường hợp đó. Các phương thức thuộc về trường hợp đó sau đó có thể làm việc với dữ liệu bạn đã xác định trên đó.

Ngược lại, các Phương thức tĩnh là không biết gì về các cá thể lớp riêng lẻ. Phương thức tĩnh tương tự như một hàm miễn phí trong C hoặc C ++. Nó không bị ràng buộc với một khởi tạo cụ thể của lớp. Đây là lý do tại sao họ không thể truy cập các giá trị cá thể. Không có ví dụ để lấy một giá trị từ!

Dữ liệu tĩnh tương tự như một phương thức tĩnh. Một giá trị được khai báo statickhông có trường hợp liên quan. Nó tồn tại cho mọi trường hợp và chỉ được khai báo ở một nơi duy nhất trong bộ nhớ. Nếu nó được thay đổi, nó sẽ thay đổi cho mọi phiên bản của lớp đó.

Một phương pháp tĩnh có thể truy cập dữ liệu tĩnh vì cả hai đều tồn tại độc lập với trường hợp cụ thể của một lớp.

Nó có thể giúp xem xét cách bạn gọi một phương thức tĩnh, so với một phương thức cá thể. Giả sử chúng ta có lớp sau (sử dụng mã giả giống như Java):

class Foo {
    // This static value belongs to the class Foo
    public static final string name = "Foo";

    // This non-static value will be unique for every instance
    private int value;

    public Foo(int value) {
         this.value = value;
    }

    public void sayValue() {
        println("Instance Value: " + value);
    }

    public static void sayName() {
        println("Static Value: " + name);
    }
}

Foo foo1 = new Foo(10);
Foo foo2 = new Foo(20);

foo1.sayValue(); // Prints "Instance Value: 10" - called on foo1
foo2.sayValue(); // Prints "Instance Value: 20" - called on foo2

Foo.sayName(); // Prints "Static Value: Foo" - called on Foo (not foo1 or foo2)

Cập nhật

Như ĐẾN TỪ điểm ra trong các ý kiến, một phương pháp tĩnh có khả năng làm việc với các dữ liệu không tĩnh, nhưng nó phải được thông qua một cách rõ ràng. Giả sử Foolớp có một phương thức khác:

public static Foo Add(Foo foo1, Foo foo2) {
    return new Foo(foo1.value + foo2.value);
}

Addvẫn còn tĩnh, và không có valuetrường hợp riêng của mình, nhưng là một thành viên của lớp Foo nó có thể truy cập vào tin valuecác lĩnh vực của truyền-in foo1foo2các trường hợp. Trong trường hợp này, chúng tôi đang sử dụng nó để trả về một giá trị mới Foo với các giá trị được thêm vào của cả hai giá trị được truyền vào.

Foo foo3 = Foo.Add(foo1, foo2); // creates a new Foo with a value of 30

30
Mở rộng trên "Không có ví dụ để có một giá trị từ" - ngay cả khi có những trường hợp, phương pháp tĩnh không thể biết dụ để có một giá trị từ.
Steve314

9
Điều này ít phức tạp hơn để giải thích bằng các ngôn ngữ không buộc mọi thứ phải là một phần của đối tượng theo mặc định.
Mason Wheeler

3
@Mason Lời nói thật. Các ngôn ngữ như Java thực thi một quan niệm sai lầm rằng một hàm là thứ nhất thiết phải thuộc về một lớp.
KChaloux

5
Đây là một câu trả lời tốt nhưng vẫn không nói được toàn bộ sự thật: các phương thức tĩnh có thể truy cập dữ liệu không tĩnh. Họ chỉ không có đối tượng ngầm hoặc this-reference có sẵn. Tôi nghĩ rằng đó là cực kỳ quan trọng để hiểu.
ĐẾN TỪ

2
@COMEFROM Ý bạn là bằng cách vượt qua rõ ràng? Tôi có thể ghi chú về nó, nếu tôi hiểu bạn chính xác. Tôi giả định rằng nó được ngụ ý rằng một phương thức tĩnh có thể truy cập một cách rõ ràng dữ liệu không tĩnh, với điều kiện là bất kỳ chức năng nào cũng có thể hoạt động trên dữ liệu được truyền một cách rõ ràng cho nó.
KChaloux

22

Hãy giải thích nó với một mẫu giả thuyết.

Hãy tưởng tượng một lớp học đơn giản:

class User
{
User(string n) { name = n; };
string name;
}

Bây giờ chúng ta tạo 2 thể hiện của lớp này:

User Bones = new User("Bones");
User Jim = new User("Jim");

bây giờ, hãy suy nghĩ - điều gì sẽ xảy ra nếu chúng ta thêm một phương thức tĩnh mới vào Người dùng, ví dụ:

static string GetName();

và bạn gọi nó là:

string x = User::GetName()

x sẽ chứa cái gì? "Jim", "Xương", hay cái gì khác?

Vấn đề là một phương thức tĩnh là một phương thức duy nhất, được định nghĩa trên lớp chứ không phải các đối tượng. Kết quả là, bạn không biết nó có thể áp dụng cho đối tượng nào. Đây là lý do tại sao nó là một điều đặc biệt. Tốt nhất nên nghĩ về các phương thức tĩnh như những thứ riêng lẻ, như các hàm trong C chẳng hạn. Các ngôn ngữ như Java có chúng chứa bên trong các lớp chủ yếu là vấn đề với Java không cho phép bất cứ thứ gì tồn tại bên ngoài một lớp, do đó, các hàm như thế này phải bị buộc trong một lớp theo một cách nào đó (hơi giống như cách main () bị buộc phải bên trong một lớp cũng vậy khi tất cả các giác quan đều nói rằng nó phải là một hàm số ít, độc lập).


2

Dữ liệu không tĩnh được liên kết với một thể hiện của lớp. Các phương thức tĩnh (và dữ liệu) không được liên kết với một thể hiện cụ thể của lớp. Không cần phải có một thể hiện của một lớp để sử dụng các phương thức tĩnh trên nó. Ngay cả khi có (các) cá thể, Java sẽ không có cách nào để đảm bảo rằng bạn đang hoạt động trên cá thể mà bạn đang mong đợi khi bạn gọi một phương thức tĩnh. Do đó, các phương thức tĩnh không thể có quyền truy cập vào dữ liệu không tĩnh.


2

Nó có thể sử dụng dữ liệu thực địa; hãy xem xét mã java sau đây:

class MyBean {
    private String myString;

    static void myStaticMethod() {
        myString = "tada";/*not allowed; if this was possible how would 
                           be different from a field without static?*/

        MyBean myBean = new MyBean();//allowed if associated with an instance
        myBean.myString = "tada";
    }
}

Mặc dù về mặt kỹ thuật này có thể là một phương thức tĩnh sử dụng dữ liệu không tĩnh, nhưng nó bỏ lỡ điểm. Tất nhiên bạn có thể tạo một thể hiện mới và truy cập nó. Nhưng điều đó không có gì để làm với staticNess.
Bobson

2
Trên thực tế, tôi nghĩ rằng đây là một bổ sung rất tốt để giải thích điểm. Nó nhấn mạnh điểm mà phương thức tĩnh cần một thể hiện của lớp trước khi nó có thể truy cập dữ liệu không tĩnh trong khi cung cấp một lý do trực quan tại sao lại như vậy.
Ben Hocking

@Bobson Bạn cũng nên đọc mã và các bình luận.
m3th0dman

@BenHocking "có" ngay cả tôi nghĩ rằng đó là điểm tốt để nói rằng "biến đối tượng luôn được liên kết với đối tượng"
JAVA

2

Tôi nghĩ vấn đề ở đây là một sự hiểu biết.

Từ quan điểm kỹ thuật, một phương thức tĩnh được gọi từ bên trong một đối tượng sẽ hoàn toàn có khả năng nhìn thấy các trường đối tượng. Tôi mạnh mẽ nghi ngờ đây là nguyên nhân gây ra câu hỏi ngay từ đầu.

Vấn đề là các phương thức có thể được gọi từ bên ngoài đối tượng. Tại thời điểm đó, không có dữ liệu cá thể để cung cấp cho họ - và do đó không có cách nào để trình biên dịch giải quyết mã. Vì việc cho phép dữ liệu cá thể gây ra mâu thuẫn, chúng tôi không được phép cho phép dữ liệu cá thể.


Tôi không đồng ý. Một phương thức tĩnh không thể truy cập dữ liệu cá thể vì dữ liệu cá thể phải được truy cập thông qua một thể hiện của đối tượng và phương thức tĩnh không được liên kết với bất kỳ thể hiện cụ thể nào (nhưng với định nghĩa lớp).
Phill W.

Bạn nhớ quan điểm của tôi. Nếu nó được gọi từ bên trong lớp, trình biên dịch có thể vượt qua một con trỏ cá thể giống như khi nó không phải là một lớp tĩnh. Vấn đề xảy ra nếu nó được gọi từ nơi khác - có nghĩa là các phương thức tĩnh riêng có thể truy cập dữ liệu cá thể (mặc dù về cơ bản là bỏ qua tĩnh.)
Loren Pechtel

Có, trình biên dịch / could / nhưng tại sao nó nên? Truyền một con trỏ như vậy về cơ bản làm giảm nó thành một phương thức cá thể. Quy định của bạn rằng chỉ có các phương thức riêng tư mới có thể thực hiện được điều này - công nghệ phản chiếu làm cho / tất cả / phương thức có thể truy cập - riêng tư hoặc không - làm cho điều này trở thành một đề xuất thậm chí còn rủi ro hơn. Những người bạn của chúng tôi ở Redmond đã đi theo một hướng khác; ngôn ngữ của họ đưa ra cảnh báo nếu bạn cố gắng gọi một phương thức tĩnh đối với một thể hiện đối tượng (chứ không phải chính lớp đó).
Phill W.

1

Hãy nghĩ về nó như các phương thức tĩnh sống trong một chiều không hướng đối tượng.

Trong "chiều hướng đối tượng", một lớp có thể sinh ra nhiều bản ngã (ví dụ), mỗi bản ngã có lương tâm của chính nó thông qua trạng thái của nó.

Trong không gian phẳng, không có OO, một lớp không biết gì về bản ngã của họ sống trong không gian OO. Thế giới của họ phẳng và mang tính thủ tục, gần như thể OOP chưa được phát minh và như thể lớp học là một chương trình thủ tục nhỏ và dữ liệu tĩnh chỉ là các biến toàn cục.


1

Tôi nghĩ cách dễ nhất để giải thích điều này là xem xét một số mã và sau đó xem xét kết quả mà chúng tôi mong đợi mã sẽ tạo ra.

// Create three new cars.  Cars have a name attribute.  
Car car1 = new Car("Mazda3");
Car car2 = new Car("FordFocus");
Car car3 = new Car("HondaFit");

// Now we would like to print the names of some cars: 
// First off why don't we try this: 

Car.printCarName();

// Expected behaviour: 
// If we think about what we are trying to do here it doesn't
// really make sense.  What instance of car name should this 
// print?  Should it print Mazda3?  FordFoucs?
// What is the expected behaviour?  If we are going to have a
// static call on car call printCarName it should probably do
// something like print all car names or a random car name or
// throw an error.  


//Now lets try this instead: 

Car.printCarName(car1);

// Expected Behaviour: 
// Luckily the expected behaviour is very clear here.  This
// should print Mazda3.  This works as expected.  


// Finally lets try this: 

car1.printMyName();

// Expected Behaviour:
// Same as previous example, however this is the *right* way
// to do it.  

Để hoàn thiện ở đây là lớp xe:

public class Car{

    public String name;

    public Car(String name){
        this.name = name;
    }

    public static printCarName(){
        print "Not sure what to do here...  Don't know which car you are talking about.";
    }

    public static printCarName(Car c){
        print c.name;
    }

    public /*NOT static*/ printMyName(){
        print this.name;
    }

}

Làm thế nào để trả lời câu hỏi này?
gnat

1
@gnat Cập nhật với ý kiến ​​để làm rõ.
Sixty feetersdude

1

Các câu trả lời khác nói lên tất cả, tuy nhiên, có một số "chi tiết" tôi muốn thêm vào.

Các phương thức tĩnh (giả sử các phương thức trong Java) chỉ không có một đối tượng ngầm liên quan đến chúng (có thể truy cập thông qua this) mà các thành viên bạn có thể truy cập thường trực tiếp bằng tên.

Điều đó không có nghĩa là họ không thể truy cập dữ liệu không tĩnh.

class MyClass {
  public static void foo(MyOtherClass object) {
    System.out.println(object.member);
  }
}
class MyOtherClass { public int member = 10; }

Tôi biết đây chỉ là một chi tiết, nhưng tôi thấy câu hỏi của bạn thật lạ khi tôi đọc nó. "Chỉ có thể sử dụng dữ liệu tĩnh" là quá nhiều hạn chế.

Nhân tiện, tôi đã không kiểm tra mã, tôi chỉ viết nó ở đây để làm gương cho những gì tôi đang nói.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.